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

use byteorder::BigEndian;
use byteorder::ByteOrder;
// NULL is exported for convenience.
use mononoke_types::sha1_hash::Sha1;
pub use mononoke_types::sha1_hash::NULL;

// Definitions for hashes 1111...1111 to ffff...ffff.

pub const ONES: Sha1 = Sha1::from_byte_array([0x11; 20]);
pub const TWOS: Sha1 = Sha1::from_byte_array([0x22; 20]);
pub const THREES: Sha1 = Sha1::from_byte_array([0x33; 20]);
pub const FOURS: Sha1 = Sha1::from_byte_array([0x44; 20]);
pub const FIVES: Sha1 = Sha1::from_byte_array([0x55; 20]);
pub const SIXES: Sha1 = Sha1::from_byte_array([0x66; 20]);
pub const SEVENS: Sha1 = Sha1::from_byte_array([0x77; 20]);
pub const EIGHTS: Sha1 = Sha1::from_byte_array([0x88; 20]);
pub const NINES: Sha1 = Sha1::from_byte_array([0x99; 20]);
pub const AS: Sha1 = Sha1::from_byte_array([0xaa; 20]);
pub const BS: Sha1 = Sha1::from_byte_array([0xbb; 20]);
pub const CS: Sha1 = Sha1::from_byte_array([0xcc; 20]);
pub const DS: Sha1 = Sha1::from_byte_array([0xdd; 20]);
pub const ES: Sha1 = Sha1::from_byte_array([0xee; 20]);
pub const FS: Sha1 = Sha1::from_byte_array([0xff; 20]);

// Definition for the hash ff...ffee..eee
pub const FS_ES: Sha1 = Sha1::from_byte_array([
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee,
    0xee, 0xee, 0xee, 0xee,
]);

/// Synthesize a hash from a byte array prefix (of up to 12 bytes) and a number.
///
/// Generates a hash where the first 12 bytes are the byte array prefix padded
/// with 0, and the following 8 bytes are the big-endian value of the number.
pub fn make_hash(prefix: &'static [u8], number: u64) -> Sha1 {
    let mut buffer = [0u8; 20];
    const NUMBER_OFFSET: usize = 20 - std::mem::size_of::<u64>();
    let prefix_len = min(prefix.len(), NUMBER_OFFSET);
    buffer[..prefix_len].copy_from_slice(&prefix[..prefix_len]);
    BigEndian::write_u64(&mut buffer[NUMBER_OFFSET..], number);
    Sha1::from_byte_array(buffer)
}

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

    #[test]
    fn verify() {
        assert_eq!(
            format!("{}", NULL),
            "0000000000000000000000000000000000000000"
        );
        assert_eq!(
            format!("{}", ONES),
            "1111111111111111111111111111111111111111"
        );
        assert_eq!(
            format!("{}", TWOS),
            "2222222222222222222222222222222222222222"
        );
        assert_eq!(
            format!("{}", THREES),
            "3333333333333333333333333333333333333333"
        );
        assert_eq!(
            format!("{}", FOURS),
            "4444444444444444444444444444444444444444"
        );
        assert_eq!(
            format!("{}", FIVES),
            "5555555555555555555555555555555555555555"
        );
        assert_eq!(
            format!("{}", SIXES),
            "6666666666666666666666666666666666666666"
        );
        assert_eq!(
            format!("{}", SEVENS),
            "7777777777777777777777777777777777777777"
        );
        assert_eq!(
            format!("{}", EIGHTS),
            "8888888888888888888888888888888888888888"
        );
        assert_eq!(
            format!("{}", NINES),
            "9999999999999999999999999999999999999999"
        );
        assert_eq!(
            format!("{}", AS),
            "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        );
        assert_eq!(
            format!("{}", BS),
            "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
        );
        assert_eq!(
            format!("{}", CS),
            "cccccccccccccccccccccccccccccccccccccccc"
        );
        assert_eq!(
            format!("{}", DS),
            "dddddddddddddddddddddddddddddddddddddddd"
        );
        assert_eq!(
            format!("{}", ES),
            "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
        );
        assert_eq!(
            format!("{}", FS),
            "ffffffffffffffffffffffffffffffffffffffff"
        );
        assert_eq!(
            format!("{}", make_hash(b"test", 0x123456)),
            "7465737400000000000000000000000000123456"
        );
        assert_eq!(
            format!("{}", make_hash(b"prefix-too-long", 0xabcdef)),
            "7072656669782d746f6f2d6c0000000000abcdef"
        );
    }
}


/*
 * 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 mod errors;
pub mod hash;
pub mod nodehash;

pub mod globalrev {
    pub use mononoke_types_mocks::globalrev::*;
}

pub mod svnrev {
    pub use mononoke_types_mocks::svnrev::*;
}


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

// Ignore deprecation of HgNodeHash::new
#![allow(deprecated)]

use mercurial_types::HgChangesetId;
use mercurial_types::HgFileNodeId;
use mercurial_types::HgManifestId;
use mercurial_types::HgNodeHash;
// NULL_HASH is exported for convenience.
pub use mercurial_types::NULL_HASH;

use crate::hash;

pub const NULL_CSID: HgChangesetId = HgChangesetId::new(NULL_HASH);

// Definitions for hashes 1111...ffff.
pub const ONES_HASH: HgNodeHash = HgNodeHash::new(hash::ONES);
pub const TWOS_HASH: HgNodeHash = HgNodeHash::new(hash::TWOS);
pub const THREES_HASH: HgNodeHash = HgNodeHash::new(hash::THREES);
pub const FOURS_HASH: HgNodeHash = HgNodeHash::new(hash::FOURS);
pub const FIVES_HASH: HgNodeHash = HgNodeHash::new(hash::FIVES);
pub const SIXES_HASH: HgNodeHash = HgNodeHash::new(hash::SIXES);
pub const SEVENS_HASH: HgNodeHash = HgNodeHash::new(hash::SEVENS);
pub const EIGHTS_HASH: HgNodeHash = HgNodeHash::new(hash::EIGHTS);
pub const NINES_HASH: HgNodeHash = HgNodeHash::new(hash::NINES);
pub const AS_HASH: HgNodeHash = HgNodeHash::new(hash::AS);
pub const BS_HASH: HgNodeHash = HgNodeHash::new(hash::BS);
pub const CS_HASH: HgNodeHash = HgNodeHash::new(hash::CS);
pub const DS_HASH: HgNodeHash = HgNodeHash::new(hash::DS);
pub const ES_HASH: HgNodeHash = HgNodeHash::new(hash::ES);
pub const FS_HASH: HgNodeHash = HgNodeHash::new(hash::FS);

// Definition for the hash ff...ffee..eee
pub const FS_ES_CSID_HASH: HgNodeHash = HgNodeHash::new(hash::FS_ES);

/// Generate a node hash from a prefix and a number.
pub fn make_hg_node_hash(prefix: &'static [u8], number: u64) -> HgNodeHash {
    HgNodeHash::new(hash::make_hash(prefix, number))
}

// Definitions for changeset IDs 1111...ffff
pub const ONES_CSID: HgChangesetId = HgChangesetId::new(ONES_HASH);
pub const TWOS_CSID: HgChangesetId = HgChangesetId::new(TWOS_HASH);
pub const THREES_CSID: HgChangesetId = HgChangesetId::new(THREES_HASH);
pub const FOURS_CSID: HgChangesetId = HgChangesetId::new(FOURS_HASH);
pub const FIVES_CSID: HgChangesetId = HgChangesetId::new(FIVES_HASH);
pub const SIXES_CSID: HgChangesetId = HgChangesetId::new(SIXES_HASH);
pub const SEVENS_CSID: HgChangesetId = HgChangesetId::new(SEVENS_HASH);
pub const EIGHTS_CSID: HgChangesetId = HgChangesetId::new(EIGHTS_HASH);
pub const NINES_CSID: HgChangesetId = HgChangesetId::new(NINES_HASH);
pub const AS_CSID: HgChangesetId = HgChangesetId::new(AS_HASH);
pub const BS_CSID: HgChangesetId = HgChangesetId::new(BS_HASH);
pub const CS_CSID: HgChangesetId = HgChangesetId::new(CS_HASH);
pub const DS_CSID: HgChangesetId = HgChangesetId::new(DS_HASH);
pub const ES_CSID: HgChangesetId = HgChangesetId::new(ES_HASH);
pub const FS_CSID: HgChangesetId = HgChangesetId::new(FS_HASH);

// Definition for the changeset ff...ffee..eee
pub const FS_ES_CSID: HgChangesetId = HgChangesetId::new(FS_ES_CSID_HASH);

/// Generate a changeset ID from a number.
pub fn make_hg_cs_id(number: u64) -> HgChangesetId {
    HgChangesetId::new(make_hg_node_hash(b"hg", number))
}

// Definitions for manifest IDs 1111...ffff
pub const ONES_MID: HgManifestId = HgManifestId::new(ONES_HASH);
pub const TWOS_MID: HgManifestId = HgManifestId::new(TWOS_HASH);
pub const THREES_MID: HgManifestId = HgManifestId::new(THREES_HASH);
pub const FOURS_MID: HgManifestId = HgManifestId::new(FOURS_HASH);
pub const FIVES_MID: HgManifestId = HgManifestId::new(FIVES_HASH);
pub const SIXES_MID: HgManifestId = HgManifestId::new(SIXES_HASH);
pub const SEVENS_MID: HgManifestId = HgManifestId::new(SEVENS_HASH);
pub const EIGHTS_MID: HgManifestId = HgManifestId::new(EIGHTS_HASH);
pub const NINES_MID: HgManifestId = HgManifestId::new(NINES_HASH);
pub const AS_MID: HgManifestId = HgManifestId::new(AS_HASH);
pub const BS_MID: HgManifestId = HgManifestId::new(BS_HASH);
pub const CS_MID: HgManifestId = HgManifestId::new(CS_HASH);
pub const DS_MID: HgManifestId = HgManifestId::new(DS_HASH);
pub const ES_MID: HgManifestId = HgManifestId::new(ES_HASH);
pub const FS_MID: HgManifestId = HgManifestId::new(FS_HASH);

// Definitions for filenode IDs 1111...ffff
pub const ONES_FNID: HgFileNodeId = HgFileNodeId::new(ONES_HASH);
pub const TWOS_FNID: HgFileNodeId = HgFileNodeId::new(TWOS_HASH);
pub const THREES_FNID: HgFileNodeId = HgFileNodeId::new(THREES_HASH);
pub const FOURS_FNID: HgFileNodeId = HgFileNodeId::new(FOURS_HASH);
pub const FIVES_FNID: HgFileNodeId = HgFileNodeId::new(FIVES_HASH);
pub const SIXES_FNID: HgFileNodeId = HgFileNodeId::new(SIXES_HASH);
pub const SEVENS_FNID: HgFileNodeId = HgFileNodeId::new(SEVENS_HASH);
pub const EIGHTS_FNID: HgFileNodeId = HgFileNodeId::new(EIGHTS_HASH);
pub const NINES_FNID: HgFileNodeId = HgFileNodeId::new(NINES_HASH);
pub const AS_FNID: HgFileNodeId = HgFileNodeId::new(AS_HASH);
pub const BS_FNID: HgFileNodeId = HgFileNodeId::new(BS_HASH);
pub const CS_FNID: HgFileNodeId = HgFileNodeId::new(CS_HASH);
pub const DS_FNID: HgFileNodeId = HgFileNodeId::new(DS_HASH);
pub const ES_FNID: HgFileNodeId = HgFileNodeId::new(ES_HASH);
pub const FS_FNID: HgFileNodeId = HgFileNodeId::new(FS_HASH);

/// Generate a file node ID from a number.
pub fn make_hg_file_node_id(number: u64) -> HgFileNodeId {
    HgFileNodeId::new(make_hg_node_hash(b"hgfilenode", number))
}


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

/// A single delta in a revlog or bundle.
///
/// The range from `start`-`end` is replaced with the `content`.
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
pub struct Delta {
    pub start: usize,
    pub end: usize,
    pub content: Vec<u8>, // need to own because of compression
}

fn snip<T>(start: usize, end: usize, slice: &[T]) -> &[T] {
    let (h, _) = slice.split_at(end);
    let (_, t) = h.split_at(start);
    t
}

/// Apply a set of `Delta`s to an input text, returning the result.
pub fn apply(text: &[u8], deltas: &[Delta]) -> Vec<u8> {
    let mut chunks = Vec::with_capacity(deltas.len() * 2);
    let mut off = 0;

    for d in deltas {
        assert!(off <= d.start);
        if off < d.start {
            chunks.push(snip(off, d.start, text));
        }
        if !d.content.is_empty() {
            chunks.push(d.content.as_ref())
        }
        off = d.end;
    }
    if off < text.len() {
        chunks.push(snip(off, text.len(), text));
    }

    let mut ret = Vec::new();
    for s in chunks {
        ret.extend_from_slice(s);
    }
    ret
}

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

    #[test]
    fn test_1() {
        let text = b"aaaa\nbbbb\ncccc\n";
        let delta = Delta {
            start: 5,
            end: 10,
            content: (&b"xxxx\n"[..]).into(),
        };
        let deltas = [delta; 1];

        let res = apply(text, &deltas[..]);
        assert_eq!(&res[..], b"aaaa\nxxxx\ncccc\n");
    }

    #[test]
    fn test_2() {
        let text = b"bbbb\ncccc\n";
        let deltas = [
            Delta {
                start: 0,
                end: 5,
                content: (&b"aaaabbbb\n"[..]).into(),
            },
            Delta {
                start: 10,
                end: 10,
                content: (&b"dddd\n"[..]).into(),
            },
        ];

        let res = apply(text, &deltas[..]);
        assert_eq!(&res[..], b"aaaabbbb\ncccc\ndddd\n");
    }

    #[test]
    fn test_3a() {
        let text = b"aaaa\nbbbb\ncccc\n";
        let deltas = [Delta {
            start: 0,
            end: 15,
            content: (&b"zzzz\nyyyy\nxxxx\n"[..]).into(),
        }];

        let res = apply(text, &deltas[..]);
        assert_eq!(&res[..], b"zzzz\nyyyy\nxxxx\n");
    }

    #[test]
    fn test_3b() {
        let text = b"aaaa\nbbbb\ncccc\n";
        let deltas = [
            Delta {
                start: 0,
                end: 5,
                content: (&b"zzzz\n"[..]).into(),
            },
            Delta {
                start: 5,
                end: 10,
                content: (&b"yyyy\n"[..]).into(),
            },
            Delta {
                start: 10,
                end: 15,
                content: (&b"xxxx\n"[..]).into(),
            },
        ];

        let res = apply(text, &deltas[..]);
        assert_eq!(&res[..], b"zzzz\nyyyy\nxxxx\n");
    }

    #[test]
    fn test_4() {
        let text = b"aaaa\nbbbb";
        let deltas = [Delta {
            start: 5,
            end: 9,
            content: (&b"bbbbcccc"[..]).into(),
        }];

        let res = apply(text, &deltas[..]);
        assert_eq!(&res[..], b"aaaa\nbbbbcccc");
    }

    #[test]
    fn test_5() {
        let text = b"aaaa\nbbbb\ncccc\n";
        let deltas = [Delta {
            start: 5,
            end: 10,
            content: (&b""[..]).into(),
        }];

        let res = apply(text, &deltas[..]);
        assert_eq!(&res[..], b"aaaa\ncccc\n");
    }
}


/*
 * 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 blobstore::BlobstoreBytes;
use bytes::Bytes;
use serde_derive::Deserialize;
use serde_derive::Serialize;

// This used to have an Extern state earlier, which stood for the hash
// being present but the content not. This state ended up never being used in
// practice, but most of the methods still return Option types because of that.
// TODO(T28296583): Clean up HgBlob APIs to not return Option types

/// Representation of a blob of data.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
#[derive(Serialize, Deserialize)]
pub struct HgBlob(Bytes);

impl HgBlob {
    pub fn new(bytes: Bytes) -> Self {
        HgBlob(bytes)
    }

    pub fn size(&self) -> usize {
        self.0.len()
    }

    pub fn as_inner(&self) -> &Bytes {
        &self.0
    }

    pub fn into_inner(self) -> Bytes {
        self.0
    }

    pub fn as_slice(&self) -> &[u8] {
        self.0.as_ref()
    }
}

impl From<Bytes> for HgBlob {
    fn from(data: Bytes) -> Self {
        HgBlob(data)
    }
}

impl From<Vec<u8>> for HgBlob {
    fn from(data: Vec<u8>) -> Self {
        HgBlob(data.into())
    }
}

/// Get a reference to the `HgBlob`'s data.
impl<'a> From<&'a HgBlob> for &'a [u8] {
    fn from(b: &'a HgBlob) -> &'a [u8] {
        b.0.as_ref()
    }
}

impl From<BlobstoreBytes> for HgBlob {
    #[inline]
    fn from(bytes: BlobstoreBytes) -> HgBlob {
        HgBlob(bytes.into_bytes())
    }
}

impl From<HgBlob> for BlobstoreBytes {
    #[inline]
    fn from(blob: HgBlob) -> BlobstoreBytes {
        BlobstoreBytes::from_bytes(blob.into_inner())
    }
}


/*
 * 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 bytes::Bytes;
use futures_old::Future;
use futures_old::Stream;
use mononoke_types::sha1_hash;
use mononoke_types::sha1_hash::Context;
use quickcheck::Arbitrary;
use quickcheck::Gen;
use serde_derive::Deserialize;
use serde_derive::Serialize;
/// Equivalent type from Mercurial's Rust code representing parents.
use types::Parents as HgTypesParents;

use crate::blob::HgBlob;
use crate::nodehash::HgNodeHash;

#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)]
#[derive(Serialize, Deserialize)]
pub enum HgParents {
    None,
    One(HgNodeHash),
    Two(HgNodeHash, HgNodeHash),
}

impl HgParents {
    pub fn new(p1: Option<HgNodeHash>, p2: Option<HgNodeHash>) -> Self {
        match (p1, p2) {
            (None, None) => HgParents::None,
            (Some(p1), None) => HgParents::One(p1),
            (None, Some(p2)) => HgParents::One(p2),
            (Some(p1), Some(p2)) => HgParents::Two(p1, p2),
        }
    }

    pub fn get_nodes(&self) -> (Option<HgNodeHash>, Option<HgNodeHash>) {
        match *self {
            HgParents::None => (None, None),
            HgParents::One(p1) => (Some(p1), None),
            HgParents::Two(p1, p2) => (Some(p1), Some(p2)),
        }
    }
}

impl Arbitrary for HgParents {
    fn arbitrary(g: &mut Gen) -> Self {
        // We make single-parent a litle more common because it's a) little simpler b) a little
        // more common anyway.
        if bool::arbitrary(g) {
            Self::new(Some(HgNodeHash::arbitrary(g)), None)
        } else if bool::arbitrary(g) {
            Self::new(
                Some(HgNodeHash::arbitrary(g)),
                Some(HgNodeHash::arbitrary(g)),
            )
        } else {
            Self::new(None, None)
        }
    }
}

/// [HgTypesParents] (an alias for [types::Parents] from Mercurial's `types` crate) is
/// the Mercurial client's Rust representation of parents. It is an enum almost identical
/// to the [HgParents] enum in this crate. As such, this conversion is only useful when
/// interacting with the Mercurial client's Rust code.
impl From<HgParents> for HgTypesParents {
    fn from(parents: HgParents) -> Self {
        match parents {
            HgParents::None => HgTypesParents::None,
            HgParents::One(p1) => HgTypesParents::One(p1.into()),
            HgParents::Two(p1, p2) => HgTypesParents::Two(p1.into(), p2.into()),
        }
    }
}

impl<'a> IntoIterator for &'a HgParents {
    type IntoIter = ParentIter;
    type Item = HgNodeHash;
    fn into_iter(self) -> ParentIter {
        ParentIter(*self)
    }
}

#[derive(Debug)]
pub struct ParentIter(HgParents);

impl Iterator for ParentIter {
    type Item = HgNodeHash;
    fn next(&mut self) -> Option<Self::Item> {
        let (ret, new) = match self.0 {
            HgParents::None => (None, HgParents::None),
            HgParents::One(p1) => (Some(p1), HgParents::None),
            HgParents::Two(p1, p2) => (Some(p1), HgParents::One(p2)),
        };
        self.0 = new;
        ret
    }
}

/// A Mercurial node backed by some data. This can represent a changeset, a manifest or a file
/// blob.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)]
#[derive(Serialize, Deserialize)]
pub struct HgBlobNode {
    blob: HgBlob,
    parents: HgParents,
}

impl HgBlobNode {
    /// Construct a node with the given content and parents.
    pub fn new<B>(blob: B, p1: Option<HgNodeHash>, p2: Option<HgNodeHash>) -> HgBlobNode
    where
        B: Into<HgBlob>,
    {
        let blob = blob.into();
        HgBlobNode {
            blob,
            parents: HgParents::new(p1, p2),
        }
    }

    pub fn size(&self) -> usize {
        self.blob.size()
    }

    pub fn as_blob(&self) -> &HgBlob {
        &self.blob
    }

    pub fn parents(&self) -> &HgParents {
        &self.parents
    }

    // Annoyingly, filenode is defined as sha1(p1 || p2 || content), not
    // sha1(p1 || p2 || sha1(content)), so we can't compute a filenode for
    // a blob we don't have
    pub fn nodeid(&self) -> HgNodeHash {
        calculate_hg_node_id(self.as_blob().as_slice(), &self.parents)
    }
}

fn hg_node_id_hash_context(parents: &HgParents) -> Context {
    let null = sha1_hash::NULL;

    let (h1, h2) = match parents {
        HgParents::None => (&null, &null),
        HgParents::One(ref p1) => (&null, &p1.0),
        HgParents::Two(ref p1, ref p2) if p1 > p2 => (&p2.0, &p1.0),
        HgParents::Two(ref p1, ref p2) => (&p1.0, &p2.0),
    };

    let mut ctxt = Context::new();

    ctxt.update(h1);
    ctxt.update(h2);

    ctxt
}

/// Compute a Hg Node ID from parents and in-place data.
pub fn calculate_hg_node_id(data: &[u8], parents: &HgParents) -> HgNodeHash {
    let mut ctxt = hg_node_id_hash_context(parents);
    ctxt.update(data);
    HgNodeHash(ctxt.finish())
}

/// Compute a Hg Node ID from parents and a stream of data.
pub fn calculate_hg_node_id_stream<S, E>(
    stream: S,
    parents: &HgParents,
) -> impl Future<Item = HgNodeHash, Error = E>
where
    S: Stream<Item = Bytes, Error = E>,
{
    let ctxt = hg_node_id_hash_context(parents);
    stream
        .fold(ctxt, |mut ctxt, bytes| {
            ctxt.update(bytes);
            Ok(ctxt)
        })
        .map(|ctxt| ctxt.finish())
        .map(HgNodeHash)
}

#[cfg(test)]
mod test {
    use bytes::BytesMut;
    use futures::compat::Future01CompatExt;
    use futures_old::stream;
    use quickcheck::quickcheck;
    use tokio::runtime::Runtime;

    use super::*;
    use crate::blob::HgBlob;

    #[test]
    fn test_node_none() {
        let blob = HgBlob::from(Bytes::from(&[0; 10][..]));
        let n = HgBlobNode::new(blob, None, None);
        assert_eq!(n.parents, HgParents::None);
    }

    #[test]
    fn test_node_one() {
        let blob = HgBlob::from(Bytes::from(&[0; 10][..]));
        let p = &HgBlobNode::new(blob.clone(), None, None);
        {
            let pid: Option<HgNodeHash> = Some(p.nodeid());
            let n = HgBlobNode::new(blob.clone(), pid, None);
            assert_eq!(n.parents, HgParents::One(pid.unwrap()));
        }
        {
            let pid: Option<HgNodeHash> = Some(p.nodeid());
            let n = HgBlobNode::new(blob.clone(), None, pid);
            assert_eq!(n.parents, HgParents::One(pid.unwrap()));
        }
        {
            let pid: Option<HgNodeHash> = Some(p.nodeid());
            let n = HgBlobNode::new(blob, pid, pid);
            assert_eq!(n.parents, HgParents::Two(pid.unwrap(), pid.unwrap()));
        }
    }

    #[test]
    fn test_node_two() {
        use std::mem;
        let mut p1 = HgBlobNode::new(HgBlob::from(Bytes::from(&b"foo1"[..])), None, None);
        let mut p2 = HgBlobNode::new(HgBlob::from(Bytes::from(&b"foo2"[..])), None, None);

        if p1 > p2 {
            mem::swap(&mut p1, &mut p2);
        }

        let pid1: Option<HgNodeHash> = Some(p1.nodeid());
        let pid2: Option<HgNodeHash> = Some(p2.nodeid());

        let node1 = {
            let n = HgBlobNode::new(HgBlob::from(Bytes::from(&b"bar"[..])), pid1, pid2);
            assert_eq!(n.parents, HgParents::Two(pid1.unwrap(), pid2.unwrap()));
            n.nodeid()
        };
        let node2 = {
            let n = HgBlobNode::new(HgBlob::from(Bytes::from(&b"bar"[..])), pid2, pid1);
            assert_eq!(n.parents, HgParents::Two(pid2.unwrap(), pid1.unwrap()));
            n.nodeid()
        };
        assert_eq!(node1, node2);
    }

    quickcheck! {
        // Verify that the two Node Id computation implementations (in place and streaming) are
        // consistent.
        fn test_node_consistency(input: Vec<Vec<u8>>, hg_parents: HgParents) -> bool {
            let rt = Runtime::new().unwrap();
            let input: Vec<Bytes> = input.into_iter().map(Bytes::from).collect();

            let stream = stream::iter_ok::<_, ()>(input.clone());

            let bytes = input.iter().fold(BytesMut::new(), |mut bytes, chunk| {
                bytes.extend_from_slice(chunk);
                bytes
            }).freeze();

            let out_inplace = calculate_hg_node_id(bytes.as_ref(), &hg_parents);
            let out_stream = rt.block_on(calculate_hg_node_id_stream(stream, &hg_parents).compat()).unwrap();

            out_inplace == out_stream
        }
    }
}


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

use anyhow::bail;
use anyhow::ensure;
use anyhow::format_err;
use anyhow::Context;
use anyhow::Result;
use quickcheck::Arbitrary;
use quickcheck::Gen;
use rand_distr::Distribution;
use rand_distr::LogNormal;

use super::delta_apply::mpatch_fold;
use super::delta_apply::wrap_deltas;
use crate::errors::ErrorKind;

#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Default)]
pub struct Delta {
    // Fragments should be in sorted order by start offset and should not overlap.
    frags: Vec<Fragment>,
}

impl Delta {
    /// Construct a new Delta object. Verify that `frags` is sane, sorted and
    /// non-overlapping.
    pub fn new(frags: Vec<Fragment>) -> Result<Self> {
        Self::verify(&frags)?;
        Ok(Delta { frags })
    }

    /// Construct a new Delta object given a fulltext (no delta).
    pub fn new_fulltext<T: Into<Vec<u8>>>(text: T) -> Self {
        Self {
            frags: vec![Fragment {
                start: 0,
                end: 0,
                content: text.into(),
            }],
        }
    }

    pub fn fragments(&self) -> &[Fragment] {
        self.frags.as_slice()
    }

    pub fn push(&mut self, frag: Fragment) {
        self.frags.push(frag);
    }

    /// If this delta might be a fulltext, return the fulltext. Note that we can only tell with
    /// certainty that something is *not* a fulltext. A delta with one fragment that inserts text
    /// in the beginning appears identical to a fulltext at this layer.
    pub fn maybe_fulltext(&self) -> Option<&[u8]> {
        if self.frags.len() == 1 && self.frags[0].start == 0 && self.frags[0].end == 0 {
            Some(self.frags[0].content.as_slice())
        } else {
            None
        }
    }

    fn verify(frags: &[Fragment]) -> Result<()> {
        let mut prev_frag: Option<&Fragment> = None;
        for (i, frag) in frags.iter().enumerate() {
            frag.verify().with_context(|| {
                ErrorKind::InvalidFragmentList(format!("invalid fragment {}", i))
            })?;
            if let Some(prev) = prev_frag {
                if frag.start < prev.end {
                    let msg = format!(
                        "fragment {}: previous end {} overlaps with start {}",
                        i, prev.end, frag.start
                    );
                    bail!(ErrorKind::InvalidFragmentList(msg));
                }
            }
            prev_frag = Some(frag);
        }
        Ok(())
    }
}

impl Arbitrary for Delta {
    fn arbitrary(g: &mut Gen) -> Self {
        let size = g.size();
        let nfrags = usize::arbitrary(g) % size;

        // Maintain invariants (start <= end, no overlap).
        let mut start = 0;
        let mut end = 0;

        let frags = (0..nfrags)
            .map(|_| {
                start = end + usize::arbitrary(g) % size;
                end = start + usize::arbitrary(g) % size;

                Fragment {
                    start,
                    end,
                    content: arbitrary_frag_content(g),
                }
            })
            .collect();
        Delta { frags }
    }

    fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
        // Not all instances generated by Vec::shrink will be
        // valid. Theoretically we could shrink in ways such that the invariants
        // are maintained, but just filtering is easier.
        Box::new(
            self.frags
                .shrink()
                .filter(|frags| Delta::verify(frags).is_ok())
                .map(|frags| Delta { frags }),
        )
    }
}

/// Represents a single contiguous modified region of text.
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub struct Fragment {
    pub start: usize,
    pub end: usize,
    pub content: Vec<u8>,
}

impl Fragment {
    /// Return the end offset of this Fragment's content, after application.
    pub fn post_end(&self) -> usize {
        self.start + self.content.len()
    }

    // Return the length of the content
    pub fn content_length(&self) -> usize {
        self.content.len()
    }

    /// Return the change in text length this Fragment will cause when applied.
    pub fn length_change(&self) -> isize {
        self.content.len() as isize - (self.end - self.start) as isize
    }

    /// Return true if the given offset falls within this Fragment's content (post-application).
    pub fn contains_offset(&self, offset: usize) -> bool {
        self.start <= offset && offset < self.post_end()
    }

    fn verify(&self) -> Result<()> {
        if self.start > self.end {
            bail!("invalid fragment: start {} > end {}", self.start, self.end);
        }
        Ok(())
    }
}

impl Arbitrary for Fragment {
    fn arbitrary(g: &mut Gen) -> Self {
        let size = g.size();

        // Maintain invariant start <= end.
        let start = usize::arbitrary(g) % size;
        let end = start + usize::arbitrary(g) % size;

        Fragment {
            start,
            end,
            content: arbitrary_frag_content(g),
        }
    }

    fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
        Box::new(
            (self.start, self.end, self.content.clone())
                .shrink()
                .filter(|&(start, end, ref _content)| {
                    // shrink could produce bad values
                    start <= end
                })
                .map(|(start, end, content)| Fragment {
                    start,
                    end,
                    content,
                }),
        )
    }
}

struct GenRngWrapper<'a> {
    gen: &'a mut Gen,
}

impl<'a> rand::RngCore for GenRngWrapper<'a> {
    fn next_u32(&mut self) -> u32 {
        u32::arbitrary(self.gen)
    }

    fn next_u64(&mut self) -> u64 {
        u64::arbitrary(self.gen)
    }

    fn fill_bytes(&mut self, dest: &mut [u8]) {
        for b in dest {
            *b = u8::arbitrary(self.gen);
        }
    }

    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> {
        self.fill_bytes(dest);
        Ok(())
    }
}

fn arbitrary_frag_content(g: &mut Gen) -> Vec<u8> {
    let size = g.size();
    // Using a uniform distribution over size here can lead to extremely bloated
    // data structures. We also want to test zero-length data with more than a
    // (1/size) probability. So use a lognormal distribution.
    //
    // The choice of mean and stdev are pretty arbitrary, but they work well for
    // common sizes (~100).
    // TODO: make this more rigorous, e.g. by using params such that p95 = size.

    let lognormal = LogNormal::new(-3.0, 2.0).expect("Failed to make LogNormal");
    let content_len = ((size as f64) * lognormal.sample(&mut GenRngWrapper { gen: g })) as usize;

    let mut v = Vec::with_capacity(content_len);
    for b in v.iter_mut() {
        *b = u8::arbitrary(g);
    }
    v
}

/// Apply a Delta to an input text, returning the result.
pub fn apply(text: &[u8], delta: &Delta) -> Result<Vec<u8>> {
    let mut chunks = Vec::with_capacity(delta.frags.len() * 2);
    let mut off = 0;

    for frag in &delta.frags {
        ensure!(
            off <= frag.start,
            "Invalid delta, fragment start is less than current offset ({} < {})",
            frag.start,
            off
        );
        if off < frag.start {
            chunks.push(text.get(off..frag.start).ok_or_else(|| {
                format_err!(
                    "Invalid delta, the range {}..{} is out of bounds for provided text",
                    off,
                    frag.start
                )
            })?);
        }
        if !frag.content.is_empty() {
            chunks.push(frag.content.as_ref())
        }
        off = frag.end;
    }
    match off.cmp(&text.len()) {
        Ordering::Greater => bail!(
            "Invalid delta, fragment is referencing out of bounds content: {} > {}",
            off,
            text.len()
        ),
        Ordering::Less => chunks.push(&text[off..text.len()]),
        Ordering::Equal => {}
    };

    let size = chunks.iter().map(|c| c.len()).sum::<usize>();
    let mut output = Vec::with_capacity(size);
    for c in chunks {
        output.extend_from_slice(c);
    }
    Ok(output)
}

/// Apply a chain of Deltas to an input text, returning the result.
/// Pack all deltas into one delta, and apply a pack to input text.
pub fn apply_chain<I: IntoIterator<Item = Delta>>(text: &[u8], deltas: I) -> Result<Vec<u8>> {
    let mut res = Vec::from(text);

    let (wrapped_deltas, data) = wrap_deltas(deltas);

    if wrapped_deltas.is_empty() {
        Ok(res)
    } else {
        // fold all deltas into one delta using logarithmic algorithm
        let folded_wrapped_delta = mpatch_fold(&wrapped_deltas, 0, wrapped_deltas.len());

        // convert into Revlog Delta
        let folded_delta = folded_wrapped_delta.into_delta(data)?;

        // apply folded delta
        res = apply(&res, &folded_delta)?;
        Ok(res)
    }
}

/// XXX: Compatibility functions for the old bdiff module for testing purposes. The delta
/// module will replace that one once all instances of Vec\<bdiff::Delta\> are replaced
/// with delta::Delta, and this compatibility module will be removed at that time.
pub mod compat {
    use super::*;
    use crate::bdiff;

    pub fn convert<T>(deltas: T) -> Delta
    where
        T: IntoIterator<Item = bdiff::Delta>,
    {
        Delta {
            frags: deltas
                .into_iter()
                .map(|delta| Fragment {
                    start: delta.start,
                    end: delta.end,
                    content: delta.content,
                })
                .collect(),
        }
    }

    pub fn apply_deltas<T>(text: &[u8], deltas: T) -> Result<Vec<u8>>
    where
        T: IntoIterator<Item = Vec<bdiff::Delta>>,
    {
        apply_chain(text, deltas.into_iter().map(convert))
    }
}

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

    use super::*;

    /// Test that fragments are verified properly.
    #[test]
    fn test_delta_new() {
        #[rustfmt::skip]
        let test_cases = vec![
            (vec![Fragment { start: 0, end: 0, content: vec![] }], true),
            (vec![Fragment { start: 0, end: 5, content: vec![] }], true),
            (vec![Fragment { start: 0, end: 5, content: vec![] },
                  Fragment { start: 5, end: 8, content: vec![] }], true),
            (vec![Fragment { start: 0, end: 5, content: vec![] },
                  Fragment { start: 6, end: 9, content: vec![] }], true),
            (vec![Fragment { start: 0, end: 5, content: vec![] },
                  Fragment { start: 6, end: 5, content: vec![] }], false),
            (vec![Fragment { start: 0, end: 5, content: vec![] },
                  Fragment { start: 4, end: 8, content: vec![] }], false),
        ];

        for (frags, success) in test_cases.into_iter() {
            let delta = Delta::new(frags);
            if success {
                assert!(delta.is_ok());
            } else {
                assert!(delta.is_err());
            }
        }
    }

    #[test]
    fn test_maybe_fulltext() {
        #[rustfmt::skip]
        let test_cases = vec![
            (vec![Fragment { start: 0, end: 0, content: vec![] }], true),
            (vec![Fragment { start: 0, end: 0, content: vec![b'a'] }], true),
            (vec![Fragment { start: 0, end: 1, content: vec![b'b'] }], false),
            (vec![Fragment { start: 1, end: 2, content: vec![b'c'] }], false),
            (vec![Fragment { start: 0, end: 0, content: vec![b'd'] },
                  Fragment { start: 1, end: 2, content: vec![b'e'] }], false),
        ];

        for (frags, maybe_fulltext) in test_cases.into_iter() {
            let delta = Delta::new(frags).unwrap();
            if maybe_fulltext {
                assert!(delta.maybe_fulltext().is_some());
            } else {
                assert!(delta.maybe_fulltext().is_none());
            }
        }
    }

    quickcheck! {
        fn delta_gen(delta: Delta) -> bool {
            Delta::verify(&delta.frags).is_ok()
        }

        fn delta_shrink(delta: Delta) -> bool {
            // This test is a bit redundant, but let's just verify.
            delta.shrink().take(100).all(|d| {
                Delta::verify(&d.frags).is_ok()
            })
        }

        fn fragment_gen(fragment: Fragment) -> bool {
            fragment.verify().is_ok()
        }

        fn fragment_shrink(fragment: Fragment) -> bool {
            fragment.shrink().take(100).all(|f| f.verify().is_ok())
        }
    }

    #[test]
    fn test_apply_1() {
        let text = b"aaaa\nbbbb\ncccc\n";
        let delta = Delta {
            frags: vec![Fragment {
                start: 5,
                end: 10,
                content: (&b"xxxx\n"[..]).into(),
            }],
        };

        let res = apply(text, &delta).unwrap();
        assert_eq!(&res[..], b"aaaa\nxxxx\ncccc\n");
    }

    #[test]
    fn test_apply_2() {
        let text = b"bbbb\ncccc\n";
        let delta = Delta {
            frags: vec![
                Fragment {
                    start: 0,
                    end: 5,
                    content: (&b"aaaabbbb\n"[..]).into(),
                },
                Fragment {
                    start: 10,
                    end: 10,
                    content: (&b"dddd\n"[..]).into(),
                },
            ],
        };

        let res = apply(text, &delta).unwrap();
        assert_eq!(&res[..], b"aaaabbbb\ncccc\ndddd\n");
    }

    #[test]
    fn test_apply_3a() {
        let text = b"aaaa\nbbbb\ncccc\n";
        let delta = Delta {
            frags: vec![Fragment {
                start: 0,
                end: 15,
                content: (&b"zzzz\nyyyy\nxxxx\n"[..]).into(),
            }],
        };

        let res = apply(text, &delta).unwrap();
        assert_eq!(&res[..], b"zzzz\nyyyy\nxxxx\n");
    }

    #[test]
    fn test_apply_3b() {
        let text = b"aaaa\nbbbb\ncccc\n";
        let delta = Delta {
            frags: vec![
                Fragment {
                    start: 0,
                    end: 5,
                    content: (&b"zzzz\n"[..]).into(),
                },
                Fragment {
                    start: 5,
                    end: 10,
                    content: (&b"yyyy\n"[..]).into(),
                },
                Fragment {
                    start: 10,
                    end: 15,
                    content: (&b"xxxx\n"[..]).into(),
                },
            ],
        };

        let res = apply(text, &delta).unwrap();
        assert_eq!(&res[..], b"zzzz\nyyyy\nxxxx\n");
    }

    #[test]
    fn test_apply_4() {
        let text = b"aaaa\nbbbb";
        let delta = Delta {
            frags: vec![Fragment {
                start: 5,
                end: 9,
                content: (&b"bbbbcccc"[..]).into(),
            }],
        };

        let res = apply(text, &delta).unwrap();
        assert_eq!(&res[..], b"aaaa\nbbbbcccc");
    }

    #[test]
    fn test_apply_5() {
        let text = b"aaaa\nbbbb\ncccc\n";
        let delta = Delta {
            frags: vec![Fragment {
                start: 5,
                end: 10,
                content: (&b""[..]).into(),
            }],
        };

        let res = apply(text, &delta).unwrap();
        assert_eq!(&res[..], b"aaaa\ncccc\n");
    }

    #[test]
    fn test_malformed_1() {
        let text = b"aaaa";
        let delta = Delta {
            frags: vec![Fragment {
                start: 5,
                end: 10,
                content: (&b""[..]).into(),
            }],
        };

        match apply(text, &delta) {
            Err(_) => {}
            Ok(res) => panic!("Unexpected success: {:?}", res),
        }
    }

    #[test]
    fn test_malformed_2() {
        let text = b"aaaa";
        let delta = Delta {
            frags: vec![Fragment {
                start: 0,
                end: 10,
                content: (&b""[..]).into(),
            }],
        };

        match apply(text, &delta) {
            Err(_) => {}
            Ok(res) => panic!("Unexpected success: {:?}", res),
        }
    }

    #[test]
    fn test_apply_chain_logarithmic1() {
        let frags1 = vec![
            Fragment {
                start: 0,
                end: 1,
                content: (&b"xx"[..]).into(),
            },
            Fragment {
                start: 1,
                end: 2,
                content: (&b"xx"[..]).into(),
            },
        ];
        let delta1 = Delta::new(frags1).unwrap();

        let frags2 = vec![Fragment {
            start: 1,
            end: 3,
            content: (&b"bb"[..]).into(),
        }];
        let delta2 = Delta::new(frags2).unwrap();

        let deltas = vec![delta1, delta2];
        let text = b"aaaaaa";

        let res = apply_chain(text, deltas).unwrap();
        assert_eq!(&res[..], b"xbbxaaaa");
    }

    #[test]
    fn test_apply_chain_logarithmic2() {
        let frags1 = vec![Fragment {
            start: 1,
            end: 2,
            content: (&b"xx"[..]).into(),
        }];
        let delta1 = Delta::new(frags1).unwrap();

        let deltas = vec![delta1];
        let text = b"aaaaaa";

        let res = apply_chain(text, deltas).unwrap();
        assert_eq!(&res[..], b"axxaaaa");
    }

    #[test]
    fn test_apply_chain_logarithmic3() {
        let frags1 = vec![
            Fragment {
                start: 5,
                end: 10,
                content: (&b"xxx"[..]).into(),
            },
            Fragment {
                start: 10,
                end: 12,
                content: (&b"yyyyy"[..]).into(),
            },
        ];
        let delta1 = Delta::new(frags1).unwrap();

        let frags2 = vec![Fragment {
            start: 6,
            end: 7,
            content: (&b"zzzzzzz"[..]).into(),
        }];
        let delta2 = Delta::new(frags2).unwrap();

        let deltas = vec![delta1, delta2];
        let text = b"aaaaabbbbbccccc";

        let res = apply_chain(text, deltas).unwrap();
        assert_eq!(&res[..], b"aaaaaxzzzzzzzxyyyyyccc");
    }

    #[test]
    fn test_apply_chain_logarithmic_append() {
        let frags1 = vec![Fragment {
            start: 1,
            end: 1,
            content: (&b"xxx"[..]).into(),
        }];
        let delta1 = Delta::new(frags1).unwrap();

        let frags2 = vec![Fragment {
            start: 4,
            end: 4,
            content: (&b"zzz"[..]).into(),
        }];
        let delta2 = Delta::new(frags2).unwrap();

        let deltas = vec![delta1, delta2];
        let text = b"a";

        let res = apply_chain(text, deltas).unwrap();

        assert_eq!(&res[..], b"axxxzzz");
    }
}


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

use anyhow::Result;
use bytes::Bytes;
use bytes::BytesMut;

use crate::delta::Delta;
use crate::delta::Fragment;

/*
 * Algorithm is taken from fbcode/scm/hg/mercurial/mpatch.c
 */

/// Wrap all Fragments and return FragmentWrapperIterator.
/// Gather all contents hold fragments contents in one vector.
pub fn wrap_deltas<I: IntoIterator<Item = Delta>>(
    deltas: I,
) -> (Vec<FragmentWrapperIterator>, Bytes) {
    let mut wrapped_deltas = Vec::new();
    let mut data = BytesMut::new();
    let mut content_offset = 0;

    for delta in deltas {
        let wrapped_delta = FragmentWrapperIterator::new(&delta, content_offset as i64);
        for frag in delta.fragments() {
            data.extend_from_slice(frag.content.as_slice());
            content_offset += frag.content.len();
        }

        wrapped_deltas.push(wrapped_delta);
    }

    (wrapped_deltas, data.freeze())
}

// Fragment Wrapper, it does not have actual data, only references to real data
#[derive(Clone, Eq, Debug, PartialEq, Ord, PartialOrd)]
pub struct FragmentWrapper {
    pub start: i64,
    pub end: i64,
    pub len: i64,
    pub content_start: i64,
}

#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Default)]
pub struct FragmentWrapperIterator {
    // Struct for holding Fragments and updating current head
    frags: Vec<FragmentWrapper>,
    cur_pointer: usize,
}

impl FragmentWrapperIterator {
    pub fn new(delta: &Delta, content_offset: i64) -> FragmentWrapperIterator {
        // Convert Delta to Vec<FragmentWrapper>, using global offset of the content in Content Bytes
        let mut frag_wrappers = Vec::new();
        let mut offset = content_offset;

        for frag in delta.fragments() {
            let frag_wrapper = FragmentWrapper {
                start: frag.start as i64,
                end: frag.end as i64,
                len: frag.content_length() as i64,
                content_start: offset,
            };
            offset += frag.content_length() as i64;
            frag_wrappers.push(frag_wrapper);
        }

        FragmentWrapperIterator {
            frags: frag_wrappers,
            cur_pointer: 0,
        }
    }

    pub fn content_length(&self) -> i64 {
        let mut size = 0;
        for frag in self.frags.as_slice() {
            size += frag.len;
        }
        size
    }

    pub fn current_fragment_mut(&mut self) -> &mut FragmentWrapper {
        &mut self.frags[self.cur_pointer]
    }

    pub fn current_fragment(&self) -> &FragmentWrapper {
        &self.frags[self.cur_pointer]
    }

    pub fn end(&self) -> bool {
        self.cur_pointer == self.frags.len()
    }

    pub fn go_next(&mut self) {
        self.cur_pointer += 1;
    }

    pub fn set_start(&mut self) {
        self.cur_pointer = 0;
    }

    pub fn fragments(&self) -> &[FragmentWrapper] {
        self.frags.as_slice()
    }

    pub fn push(&mut self, frag: FragmentWrapper) {
        self.frags.push(frag);
    }

    pub fn into_delta(&self, data: Bytes) -> Result<Delta> {
        let mut frags = Vec::new();

        for frag_wrapper in self.frags.as_slice() {
            let content_start = frag_wrapper.content_start as usize;
            let content_end = (frag_wrapper.content_start + frag_wrapper.len) as usize;

            let frag = Fragment {
                start: frag_wrapper.start as usize,
                end: frag_wrapper.end as usize,
                content: data.slice(content_start..content_end).to_vec(),
            };
            frags.push(frag);
        }
        Delta::new(frags)
    }
}

/// Merge 2 sequential deltas into 1 delta
fn combine(
    a: &mut FragmentWrapperIterator,
    b: &mut FragmentWrapperIterator,
) -> FragmentWrapperIterator {
    let mut combined: FragmentWrapperIterator = Default::default();
    let mut offset = 0;
    let mut post;

    a.set_start();
    for b_frag in b.fragments() {
        offset = gather(&mut combined, a, b_frag.start, offset);

        post = discard(a, b_frag.end, offset);

        let frag = FragmentWrapper {
            start: b_frag.start - offset,
            end: b_frag.end - post,
            len: b_frag.len,
            content_start: b_frag.content_start,
        };
        combined.push(frag);
        offset = post;
    }

    // process tail
    while !a.end() {
        combined.push(a.current_fragment().clone());
        a.go_next();
    }
    combined
}

/// Copy all fragments from src to dst until cut
fn gather(
    dst: &mut FragmentWrapperIterator,
    src: &mut FragmentWrapperIterator,
    cut: i64,
    mut offset: i64,
) -> i64 {
    while !src.end() {
        let frag = src.current_fragment().clone();

        if frag.start + offset >= cut {
            break;
        }

        let postend = offset + frag.start + frag.len;
        if postend <= cut {
            offset += frag.start + frag.len - frag.end;
            dst.push(frag.clone());

            src.go_next();
        } else {
            let new_start = cmp::min(cut - offset, frag.end);
            let prev_len = cmp::min(cut - offset - frag.start, frag.len);

            offset += frag.start + prev_len - new_start;

            let prev_content_start = frag.content_start;
            let new_content_start = frag.content_start + prev_len;

            let new_frag = FragmentWrapper {
                start: frag.start,
                end: new_start,
                len: prev_len,
                content_start: prev_content_start,
            };

            dst.push(new_frag);

            let frag_mut = src.current_fragment_mut();

            frag_mut.start = new_start;
            frag_mut.len = frag.len - prev_len;
            frag_mut.content_start = new_content_start;
            break;
        }
    }
    offset
}

/// Delete all fragments from src until cut
fn discard(src: &mut FragmentWrapperIterator, cut: i64, mut offset: i64) -> i64 {
    while !src.end() {
        let frag = src.current_fragment().clone();

        if frag.start + offset >= cut {
            break;
        }

        let postend = offset + frag.start + frag.len;
        if postend <= cut {
            offset += frag.start + frag.len - frag.end;
            src.go_next();
        } else {
            let new_start = cmp::min(cut - offset, frag.end);
            let prev_len = cmp::min(cut - offset - frag.start, frag.len);

            offset += frag.start + prev_len - new_start;

            let new_content_start = frag.content_start + prev_len;

            let frag_mut = src.current_fragment_mut();

            frag_mut.start = new_start;
            frag_mut.len = frag.len - prev_len;
            frag_mut.content_start = new_content_start;
            break;
        }
    }
    offset
}

/// Fold deltas in the range [start, end)
pub fn mpatch_fold(
    deltas: &Vec<FragmentWrapperIterator>,
    start: usize,
    end: usize,
) -> FragmentWrapperIterator {
    assert!(start < end);

    if start + 1 == end {
        deltas[start].clone()
    } else {
        let half_deltas_cnt = (end - start) / 2;
        combine(
            &mut mpatch_fold(deltas, start, start + half_deltas_cnt),
            &mut mpatch_fold(deltas, start + half_deltas_cnt, end),
        )
    }
}


/*
 * 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(Debug, Error)]
pub enum ErrorKind {
    #[error("invalid sha-1 input: {0}")]
    InvalidSha1Input(String),
    #[error("invalid fragment list: {0}")]
    InvalidFragmentList(String),
    #[error("invalid Thrift structure '{0}': {1}")]
    InvalidThrift(String, String),
    #[error("error while deserializing blob for '{0}'")]
    BlobDeserializeError(String),
    #[error("imposssible to parse unknown rev flags")]
    UnknownRevFlags,
}


/*
 * 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 bytes::Bytes;
use quickcheck::single_shrinker;
use quickcheck::Arbitrary;
use quickcheck::Gen;

/// Contents of a Mercurial file, stripped of any inline metadata.
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
pub struct FileBytes(pub Bytes);

impl FileBytes {
    pub fn size(&self) -> usize {
        self.0.len()
    }

    /// Whether this starts with a particular string.
    #[inline]
    pub fn starts_with(&self, needle: &[u8]) -> bool {
        self.0.starts_with(needle)
    }

    pub fn into_bytes(self) -> Bytes {
        self.0
    }

    pub fn as_bytes(&self) -> &Bytes {
        &self.0
    }
}

impl IntoIterator for FileBytes {
    type Item = <Bytes as IntoIterator>::Item;
    type IntoIter = <Bytes as IntoIterator>::IntoIter;

    fn into_iter(self) -> Self::IntoIter {
        self.0.into_iter()
    }
}

impl Arbitrary for FileBytes {
    fn arbitrary(g: &mut Gen) -> Self {
        FileBytes(Vec::arbitrary(g).into())
    }

    fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
        single_shrinker(FileBytes(vec![].into()))
    }
}


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

use anyhow::Result;
use bitflags::bitflags;

use crate::errors::ErrorKind;

bitflags! {
    // names are from hg revlog.py
    pub struct RevFlags: u16 {
        const REVIDX_DEFAULT_FLAGS = 0;
        const REVIDX_EXTSTORED = 1 << 13;  // data is stored externally
        // Unused, not supported yet
        const REVIDX_ELLIPSIS = 1 << 14;  // revision hash does not match data (narrowhg)
    }
}

pub fn parse_rev_flags(flags: Option<u16>) -> Result<RevFlags> {
    // None -> Default
    // Some(valid) -> Ok(valid_flags)
    // Some(invalid) -> Err()
    match flags {
        Some(value) => match RevFlags::from_bits(value) {
            Some(value) => Ok(value),
            None => Err(ErrorKind::UnknownRevFlags.into()),
        },
        None => Ok(RevFlags::REVIDX_DEFAULT_FLAGS),
    }
}

impl fmt::Display for RevFlags {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.bits())
    }
}

impl From<RevFlags> for u64 {
    fn from(f: RevFlags) -> u64 {
        f.bits().into()
    }
}


/*
 * 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::cmp;
use std::ffi::OsStr;
use std::os::unix::ffi::OsStrExt;
use std::path::PathBuf;

use mononoke_types::sha1_hash::Sha1;

fn fsencode_filter<P: AsRef<[u8]>>(p: P, dotencode: bool) -> String {
    let p = p.as_ref();
    let p = fnencode(p, true);
    let p = auxencode(p, dotencode);
    String::from_utf8(p).expect("bad utf8")
}

fn fsencode_dir_impl<'a, Iter>(dotencode: bool, iter: Iter) -> PathBuf
where
    Iter: Iterator<Item = &'a &'a [u8]>,
{
    iter.map(|p| fsencode_filter(direncode(p), dotencode))
        .collect()
}

const MAXSTOREPATHLEN: usize = 120;

/// Perform the mapping to a filesystem path used in a .hg directory
/// Assumes that this path is a file.
/// This encoding is used when both 'store' and 'fncache' requirements are in the repo.
pub fn fncache_fsencode(elements: &[&[u8]], dotencode: bool) -> PathBuf {
    let mut path = elements.iter().rev();
    let file = path.next();
    let path = path.rev();
    let mut ret: PathBuf = fsencode_dir_impl(dotencode, path.clone());

    if let Some(basename) = file {
        ret.push(fsencode_filter(basename, dotencode));
        let os_str: &OsStr = ret.as_ref();
        if os_str.as_bytes().len() > MAXSTOREPATHLEN {
            hashencode(path.copied().collect(), basename, dotencode)
        } else {
            ret.clone()
        }
    } else {
        PathBuf::new()
    }
}

/// Perform the mapping to a filesystem path used in a .hg directory
/// Assumes that this path is a file.
/// This encoding is used when 'store' requirement is present in the repo, but 'fncache'
/// requirement is not present.
pub fn simple_fsencode(elements: &[&[u8]]) -> PathBuf {
    let mut path = elements.iter().rev();
    let file = path.next();
    let directory_elements = path.rev();

    if let Some(basename) = file {
        let encoded_directory: PathBuf = directory_elements
            .map(|elem| {
                let encoded_element = fnencode(direncode(elem), false);
                String::from_utf8(encoded_element).expect("bad utf8")
            })
            .collect();

        let encoded_basename =
            PathBuf::from(String::from_utf8(fnencode(basename, false)).expect("bad utf8"));
        encoded_directory.join(encoded_basename)
    } else {
        PathBuf::new()
    }
}

static HEX: &[u8] = b"0123456789abcdef";

#[allow(clippy::identity_op)]
fn hexenc(byte: u8, out: &mut Vec<u8>) {
    out.push(b'~');
    out.push(HEX[((byte >> 4) & 0xf) as usize]);
    out.push(HEX[((byte >> 0) & 0xf) as usize]);
}

// Encode directory names
fn direncode(elem: &[u8]) -> Vec<u8> {
    let mut ret = Vec::new();

    ret.extend_from_slice(elem);
    if elem.ends_with(b".hg") || elem.ends_with(b".i") || elem.ends_with(b".d") {
        ret.extend_from_slice(b".hg")
    }

    ret
}

// If this is not for fncache then path elements longer than 255 in this encoding will not use
// UPPERCASE -> _lowercase encoding, as per D8527475
// If this is not for fncache and previous encoding scheme result in longer then 255
// _(underscore) -> :(semicolon), as per D9967059
fn fnencode<E: AsRef<[u8]>>(elem: E, forfncache: bool) -> Vec<u8> {
    enum UnderscoreEncoding {
        EncodeTo(&'static [u8]),
    }

    enum UpperEncoding {
        ToUnderscoreAndLower,
        ToUpper,
    }

    fn upper_to_underscore_and_lower(ret: &mut Vec<u8>, e: u8) {
        ret.push(b'_');
        ret.push(e - b'A' + b'a');
    }

    fn upper_to_upper(ret: &mut Vec<u8>, e: u8) {
        ret.push(e);
    }

    fn fnencode_internal(
        elem: &[u8],
        encode_upper: UpperEncoding,
        encode_underscore: UnderscoreEncoding,
    ) -> Vec<u8> {
        let mut ret = Vec::new();

        for e in elem {
            let e = *e;
            match e {
                0..=31 | 126..=255 => hexenc(e, &mut ret),
                b'\\' | b':' | b'*' | b'?' | b'"' | b'<' | b'>' | b'|' => hexenc(e, &mut ret),
                b'A'..=b'Z' => match encode_upper {
                    UpperEncoding::ToUnderscoreAndLower => {
                        upper_to_underscore_and_lower(&mut ret, e)
                    }
                    UpperEncoding::ToUpper => upper_to_upper(&mut ret, e),
                },
                b'_' => match encode_underscore {
                    UnderscoreEncoding::EncodeTo(slice) => ret.extend_from_slice(slice),
                },
                _ => ret.push(e),
            }
        }

        ret
    }

    let elem = elem.as_ref();
    let ret = fnencode_internal(
        elem,
        UpperEncoding::ToUnderscoreAndLower,
        UnderscoreEncoding::EncodeTo(b"__"),
    );

    if !forfncache && ret.len() > 255 {
        let encoded_upper = fnencode_internal(
            elem,
            UpperEncoding::ToUpper,
            UnderscoreEncoding::EncodeTo(b"__"),
        );
        if encoded_upper.len() > 255 {
            fnencode_internal(
                elem,
                UpperEncoding::ToUpper,
                UnderscoreEncoding::EncodeTo(b":"),
            )
        } else {
            encoded_upper
        }
    } else {
        ret
    }
}

fn lowerencode(elem: &[u8]) -> Vec<u8> {
    let mut ret = Vec::new();

    for e in elem {
        let e = *e;
        match e {
            0..=31 | 126..=255 => hexenc(e, &mut ret),
            b'\\' | b':' | b'*' | b'?' | b'"' | b'<' | b'>' | b'|' => hexenc(e, &mut ret),
            b'A'..=b'Z' => {
                ret.push(e - b'A' + b'a');
            }
            _ => ret.push(e),
        }
    }

    ret
}

// if path element is a reserved windows name, remap last character to ~XX
fn auxencode<E: AsRef<[u8]>>(elem: E, dotencode: bool) -> Vec<u8> {
    let elem = elem.as_ref();
    let mut ret = Vec::new();

    if let Some((first, elements)) = elem.split_first() {
        if dotencode && (first == &b'.' || first == &b' ') {
            hexenc(*first, &mut ret);
            ret.extend_from_slice(elements);
        } else {
            // if base portion of name is a windows reserved name,
            // then hex encode 3rd char
            let pos = elem.iter().position(|c| *c == b'.').unwrap_or(elem.len());
            let prefix_len = ::std::cmp::min(3, pos);
            match &elem[..prefix_len] {
                b"aux" | b"con" | b"prn" | b"nul" if pos == 3 => {
                    ret.extend_from_slice(&elem[..2]);
                    hexenc(elem[2], &mut ret);
                    ret.extend_from_slice(&elem[3..]);
                }
                b"com" | b"lpt" if pos == 4 && elem[3] >= b'1' && elem[3] <= b'9' => {
                    ret.extend_from_slice(&elem[..2]);
                    hexenc(elem[2], &mut ret);
                    ret.extend_from_slice(&elem[3..]);
                }
                _ => ret.extend_from_slice(elem),
            }
        }
    }
    // hex encode trailing '.' or ' '
    if let Some(last) = ret.pop() {
        if last == b'.' || last == b' ' {
            hexenc(last, &mut ret);
        } else {
            ret.push(last);
        }
    }

    ret
}

/// Returns file extension with period; leading periods are ignored
///
/// # Examples
/// ```ignore
/// assert_eq(get_extension(b".foo"), b"");
/// assert_eq(get_extension(b"bar.foo"), b".foo");
/// assert_eq(get_extension(b"foo."), b".");
///
/// ```
fn get_extension(basename: &[u8]) -> &[u8] {
    let idx = basename
        .iter()
        .enumerate()
        .rev()
        .find(|&(_, c)| *c == b'.')
        .map(|(idx, _)| idx);
    match idx {
        None | Some(0) => b"",
        Some(idx) => &basename[idx..],
    }
}

/// Returns sha-1 hash of the file name
///
/// # Example
/// ```ignore
/// let dirs = vec![Vec::from(&b"asdf"[..]), Vec::from("asdf")];
/// let file = b"file.txt";
/// assert_eq!(hashed_file(&dirs, Some(file)), Sha1::from(&b"asdf/asdf/file.txt"[..]));
///
/// ```
fn hashed_file(dirs: &[&[u8]], file: &[u8]) -> Sha1 {
    let mut elements: Vec<_> = dirs.iter().map(|elem| direncode(elem)).collect();
    elements.push(Vec::from(file));

    Sha1::from(elements.join(&b'/').as_ref())
}

/// This function emulates core mercurial _hashencode() function. In core mercurial it is used
/// if path is longer than MAXSTOREPATHLEN.
/// Resulting path starts with "dh/" prefix, it has the same extension as initial file, and it
/// also contains sha-1 hash of the initial file.
/// Each path element is encoded to avoid file name collisions, and they are combined
/// in such way that resulting path is shorter than MAXSTOREPATHLEN.
fn hashencode(dirs: Vec<&[u8]>, file: &[u8], dotencode: bool) -> PathBuf {
    let sha1 = hashed_file(&dirs, file);

    let mut parts = dirs
        .iter()
        .map(|elem| direncode(elem))
        .map(|elem| lowerencode(&elem))
        .map(|elem| auxencode(elem, dotencode));

    let mut shortdirs = Vec::new();
    // Prefix (which is usually 'data' or 'meta') is replaced with 'dh'.
    // Other directories will be converted.
    let prefix = parts.next();
    let prefix_len = prefix.map_or(0, |elem| elem.len());
    // Each directory is no longer than `dirprefixlen`, and total length is less than
    // `maxshortdirslen`. These constants are copied from core mercurial code.
    let dirprefixlen = 8;
    let maxshortdirslen = 8 * (dirprefixlen + 1) - prefix_len;
    let mut shortdirslen = 0;
    for p in parts {
        let size = cmp::min(dirprefixlen, p.len());
        let dir = &p[..size];
        let dir = match dir.split_last() {
            Some((last, prefix)) => {
                if last == &b'.' || last == &b' ' {
                    let mut vec = Vec::from(prefix);
                    vec.push(b'_');
                    vec
                } else {
                    Vec::from(dir)
                }
            }
            _ => Vec::from(dir),
        };

        if shortdirslen == 0 {
            shortdirslen = dir.len();
        } else {
            // 1 is for '/'
            let t = shortdirslen + 1 + dir.len();
            if t > maxshortdirslen {
                break;
            }
            shortdirslen = t;
        }
        shortdirs.push(dir);
    }
    let mut shortdirs = shortdirs.join(&b'/');
    if !shortdirs.is_empty() {
        shortdirs.push(b'/');
    }

    // File name encoding is a bit different from directory element encoding - direncode() is not
    // applied.
    let basename = auxencode(lowerencode(file), dotencode);
    let hex_sha = sha1.to_hex();

    let mut res = vec![];
    res.push(&b"dh/"[..]);
    res.push(&shortdirs);
    // filler is inserted after shortdirs but before sha. Let's remember the index.
    // This is part of the basename that is as long as possible given that the resulting string
    // is shorter that MAXSTOREPATHLEN.
    let filler_index = res.len();
    res.push(hex_sha.as_bytes());
    res.push(get_extension(&basename));

    let filler = {
        let current_len = res.iter().map(|elem| elem.len()).sum::<usize>();
        let spaceleft = MAXSTOREPATHLEN - current_len;
        let size = cmp::min(basename.len(), spaceleft);
        &basename[..size]
    };
    res.insert(filler_index, filler);
    PathBuf::from(String::from_utf8(res.concat()).expect("bad utf8"))
}

#[cfg(test)]
mod test {
    use mononoke_types::MPath;
    use mononoke_types::MPathElement;

    use super::*;

    fn check_fsencode(path: &[u8], expected: &str) {
        let mut elements = vec![];
        let path = &MPath::new(path).unwrap();
        elements.extend(path.into_iter().cloned());

        let elements = elements.iter().map(|e| e.as_ref()).collect::<Vec<_>>();
        assert_eq!(fncache_fsencode(&elements, false), PathBuf::from(expected));
    }

    fn check_simple_fsencode(path: &[u8], expected: &str) {
        let mut elements = vec![];
        let path = &MPath::new(path).unwrap();
        elements.extend(path.into_iter().cloned());

        let elements = elements.iter().map(|e| e.as_ref()).collect::<Vec<_>>();
        assert_eq!(simple_fsencode(&elements), PathBuf::from(expected));
    }

    #[test]
    fn fsencode_simple() {
        check_fsencode(b"foo/bar", "foo/bar");
    }

    #[test]
    fn fsencode_simple_single() {
        check_fsencode(b"bar", "bar");
    }

    #[test]
    fn fsencode_hexquote() {
        check_fsencode(b"oh?/wow~:<>", "oh~3f/wow~7e~3a~3c~3e");
    }

    #[test]
    fn fsencode_direncode() {
        check_fsencode(b"foo.d/bar.d", "foo.d.hg/bar.d");
        check_fsencode(b"foo.d/bar.d/file", "foo.d.hg/bar.d.hg/file");
        check_fsencode(b"tests/legacy-encoding.hg", "tests/legacy-encoding.hg");
        check_fsencode(
            b"tests/legacy-encoding.hg/file",
            "tests/legacy-encoding.hg.hg/file",
        );
    }

    #[test]
    fn fsencode_direncode_single() {
        check_fsencode(b"bar.d", "bar.d");
    }

    #[test]
    fn fsencode_upper() {
        check_fsencode(b"HELLO/WORLD", "_h_e_l_l_o/_w_o_r_l_d");
    }

    #[test]
    fn fsencode_upper_direncode() {
        check_fsencode(b"HELLO.d/WORLD.d", "_h_e_l_l_o.d.hg/_w_o_r_l_d.d");
    }

    #[test]
    fn fsencode_single_underscore_fileencode() {
        check_fsencode(b"_", "__");
    }

    #[test]
    fn fsencode_auxencode() {
        check_fsencode(b"com3", "co~6d3");
        check_fsencode(b"lpt9", "lp~749");
        check_fsencode(b"com", "com");
        check_fsencode(b"lpt.3", "lpt.3");
        check_fsencode(b"com3x", "com3x");
        check_fsencode(b"xcom3", "xcom3");
        check_fsencode(b"aux", "au~78");
        check_fsencode(b"auxx", "auxx");
        check_fsencode(b" ", "~20");
        check_fsencode(b"aux ", "aux~20");
    }

    fn join_and_check(prefix: Option<&str>, suffix: Option<&str>, expected: &str) {
        let prefix = prefix.map(|prefix| MPath::new(prefix).unwrap());
        let suffix = suffix.map(|suffix| MPath::new(suffix).unwrap());
        let mut elements = vec![];
        let joined = MPath::join_opt(prefix.as_ref(), MPath::iter_opt(suffix.as_ref()));
        elements.extend(MPath::into_iter_opt(joined));
        let elements = elements.iter().map(|e| e.as_ref()).collect::<Vec<_>>();
        assert_eq!(fncache_fsencode(&elements, false), PathBuf::from(expected));
    }

    #[test]
    fn join() {
        join_and_check(Some("prefix"), Some("suffix"), "prefix/suffix");
        join_and_check(Some("prefix"), None, "prefix");
        join_and_check(None, Some("suffix"), "suffix");

        assert_eq!(MPath::new(b"asdf").unwrap().join(None).to_vec().len(), 4);

        assert_eq!(
            MPath::new(b"asdf")
                .unwrap()
                .join(&MPathElement::new(b"bdc".to_vec()).expect("valid MPathElement"))
                .to_vec()
                .len(),
            8
        );
    }

    #[test]
    fn test_get_extension() {
        assert_eq!(get_extension(b".foo"), b"");
        assert_eq!(get_extension(b"foo."), b".");
        assert_eq!(get_extension(b"foo"), b"");
        assert_eq!(get_extension(b"foo.txt"), b".txt");
        assert_eq!(get_extension(b"foo.bar.blat"), b".blat");
    }

    #[test]
    fn test_hashed_file() {
        let dirs = vec![&b"asdf"[..], b"asdf"];
        let file = b"file.txt";
        assert_eq!(
            hashed_file(&dirs, file),
            Sha1::from(&b"asdf/asdf/file.txt"[..])
        );
    }

    #[test]
    fn test_fsencode() {
        let toencode = b"data/abcdefghijklmnopqrstuvwxyz0123456789 !#%&'()+,-.;=[]^`{}";
        let expected = "data/abcdefghijklmnopqrstuvwxyz0123456789 !#%&'()+,-.;=[]^`{}";
        check_fsencode(&toencode[..], expected);

        let toencode = b"data/\x02\x03\x04\x05\x06\x07\x08\t\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f";
        let expected = "data/~02~03~04~05~06~07~08~09~0b~0c~0d~0e~0f~10~11~12~13~14~15~16~17~18~19~1a~1b~1c~1d~1e~1f";
        check_fsencode(&toencode[..], expected);
    }

    #[test]
    fn test_simple_fsencode() {
        let toencode: &[u8] = b"foo.i/bar.d/bla.hg/hi:world?/HELLO";
        let expected = "foo.i.hg/bar.d.hg/bla.hg.hg/hi~3aworld~3f/_h_e_l_l_o";

        check_simple_fsencode(toencode, expected);

        let toencode: &[u8] = b".arcconfig.i";
        let expected = ".arcconfig.i";
        check_simple_fsencode(toencode, expected);
    }

    /// Tested as in D8527475
    #[test]
    fn test_very_long_simple_fsencode() {
        let toencode = vec![b'X'; 128];
        let expected = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";

        check_simple_fsencode(&toencode, expected);

        let toencode = vec![b'X'; 127];
        let expected = "_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x";

        check_simple_fsencode(&toencode, expected);

        let mut toencode = vec![b'Z', b'/'];
        toencode.append(&mut vec![b'X'; 128]);
        toencode.push(b'/');
        toencode.append(&mut vec![b'Y'; 127]);
        let expected = "_z/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y_y";

        check_simple_fsencode(&toencode, expected);
    }

    // Tested as in D9967059
    #[test]
    fn test_hg() {
        let mut toencode = vec![b'X'; 253];
        toencode.push(b'_');
        let expected = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX__";
        check_simple_fsencode(&toencode, expected);

        let mut toencode = vec![b'X'; 254];
        toencode.push(b'_');
        let expected = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:";
        check_simple_fsencode(&toencode, expected);

        let x = vec![vec![b'X', b'_']; 85];
        let mut x_flatten: Vec<u8> = x.iter().flat_map(|array| array.iter()).cloned().collect();
        let y = vec![vec![b'Y', b'_']; 86];
        let mut y_flatten: Vec<u8> = y.iter().flat_map(|array| array.iter()).cloned().collect();

        let mut toencode = vec![b'Z', b'/'];
        toencode.append(&mut x_flatten);
        toencode.push(b'/');
        toencode.append(&mut y_flatten);
        let expected = "_z/X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__X__/Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:Y:";
        check_simple_fsencode(&toencode, expected);

        let toencode: &[u8] = b"X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X";
        let expected = "X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X";
        check_simple_fsencode(toencode, expected);

        let toencode: &[u8] = b"A/Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y__Y/ZZZ/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
        let expected = "_a/Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y::Y/_z_z_z/_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x";
        check_simple_fsencode(toencode, expected);
    }
}


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

//! Mercurial Types
//!
//! This crate contains useful definitions for types that occur in Mercurial. Or more generally,
//! in a source control system that is based on Mercurial and extensions.
//!
//! The top-most level is the Repo, which is a container for changesets.
//!
//! A changeset represents a snapshot of a file tree at a specific moment in time. Changesets
//! can (and commonly do) have parent-child relationships with other changesets; if once changeset
//! is the child of another one, then it is interpreted as an incremental change in the history of
//! a single namespace. Changesets can have multiple parents (currently limited to 2), which
//! represents the merging of history. A changeset can have no parents, which represents the
//! creation of a new namespace. There's no requirement that all (or any) changeset within a
//! repo be connected at all via parent-child relationships.
//!
//! Each changeset has a tree of manifests, which represent their namespace. A manifest is
//! equivalent to a directory in a filesystem, mapping names to other objects. Those other
//! objects can be other manifests (subdirectories), files, or symlinks. Manifest objects can
//! be shared by multiple changesets - if the only difference between two changesets is a
//! single file, then all other files and directories will be the same and shared.
//!
//! Changesets, manifests and files are uniformly represented by a `Node`. A `Node` has
//! 0-2 parents and some content. A node's identity is computed by hashing over (p1, p2, content),
//! resulting in `HgNodeHash` (TODO: rename HgNodeHash -> NodeId?). This means manifests and files
//! have a notion of history independent of the changeset(s) they're embedded in.
//!
//! Nodes are stored as blobs in the blobstore, but with their content in a separate blob. This
//! is because it's very common for the same file content to appear either under different names
//! (copies) or multiple times within the same history (reverts), or both (rebase, amend, etc).
//!
//! Blobs are the underlying raw storage for all immutable objects in Mononoke. Their primary
//! storage key is a hash (TBD, stronger than SHA1) over their raw bit patterns, but they can
//! have other keys to allow direct access via multiple aliases. For example, file content may be
//! shared by multiple nodes, but can be access directly without having to go via a node.
//!
//! Delta and bdiff are used in revlogs and on the wireprotocol to represent inter-file
//! differences. These are for interfacing at the edges, but are not used within Mononoke's core
//! structures at all.

pub mod bdiff;
pub mod blob;
pub mod blobnode;
pub mod blobs;
pub mod delta;
pub mod delta_apply;
pub mod envelope;
pub mod errors;
pub mod file;
pub mod flags;
pub mod fsencode;
pub mod manifest;
mod node;
pub mod nodehash;
pub mod remotefilelog;
pub mod sql_types;
pub mod utils;

pub use blob::HgBlob;
pub use blobnode::calculate_hg_node_id;
pub use blobnode::calculate_hg_node_id_stream;
pub use blobnode::HgBlobNode;
pub use blobnode::HgParents;
pub use blobs::fetch_manifest_envelope;
pub use blobs::fetch_manifest_envelope_opt;
pub use blobs::fetch_raw_manifest_bytes;
pub use blobs::HgBlobEnvelope;
pub use delta::Delta;
pub use envelope::HgChangesetEnvelope;
pub use envelope::HgChangesetEnvelopeMut;
pub use envelope::HgFileEnvelope;
pub use envelope::HgFileEnvelopeMut;
pub use envelope::HgManifestEnvelope;
pub use envelope::HgManifestEnvelopeMut;
pub use errors::ErrorKind;
// Re-exports from mononoke_types. Eventually these should go away and everything should depend
// directly on mononoke_types;
pub use file::FileBytes;
pub use flags::parse_rev_flags;
pub use flags::RevFlags;
pub use fsencode::fncache_fsencode;
pub use fsencode::simple_fsencode;
pub use mononoke_types::sha1_hash;
pub use mononoke_types::FileType;
pub use mononoke_types::Globalrev;
pub use mononoke_types::MPath;
pub use mononoke_types::MPathElement;
pub use mononoke_types::RepoPath;
pub use node::Node;
pub use nodehash::HgChangesetId;
pub use nodehash::HgChangesetIdPrefix;
pub use nodehash::HgChangesetIdsResolvedFromPrefix;
pub use nodehash::HgEntryId;
pub use nodehash::HgFileNodeId;
pub use nodehash::HgManifestId;
pub use nodehash::HgNodeHash;
pub use nodehash::HgNodeKey;
pub use nodehash::NULL_CSID;
pub use nodehash::NULL_HASH;
pub use remotefilelog::convert_parents_to_remotefilelog_format;
pub use remotefilelog::HgFileHistoryEntry;
pub use utils::percent_encode;

pub use self::manifest::Type;

#[cfg(test)]
mod test;

mod thrift {
    pub use mercurial_thrift::*;
    pub use mononoke_types_thrift::*;
}


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

use mononoke_types::FileType;
use serde_derive::Serialize;

/// Type of an Entry
///
/// File and Executable are identical - they both represent files containing arbitrary content.
/// The only difference is that the Executables are created with executable permission when
/// checked out.
///
/// Symlink is also the same as File, but the content of the file is interpolated into a path
/// being traversed during lookup.
///
/// Tree is a reference to another Manifest (directory-like) object.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize)]
pub enum Type {
    File(FileType),
    Tree,
}

impl Type {
    #[inline]
    pub fn is_tree(&self) -> bool {
        self == &Type::Tree
    }

    pub fn manifest_suffix(&self) -> &'static str {
        // It's a little weird that this returns a Unicode string and not a bytestring, but that's
        // what callers demand.
        match self {
            Type::Tree => "t",
            Type::File(FileType::Symlink) => "l",
            Type::File(FileType::Executable) => "x",
            Type::File(FileType::Regular) => "",
        }
    }
}

impl fmt::Display for Type {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Type::Tree => write!(f, "tree"),
            Type::File(ft) => write!(f, "{}", ft),
        }
    }
}


/*
 * 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 anyhow::Error;
use futures_old::Future;
use futures_old::Stream;

/// A general source control Node
///
/// A `Node` has some content, and some number of `HgParents` (immediate ancestors).
/// For Mercurial this is constrained to [0, 2] parents, but other scms (ie Git) can have
/// arbitrary numbers of parents.
///
/// NOTE: Unless you're writing code that should be general across multiple source control
/// systems, don't use Node. For example, use HgBlobNode or manifest::Entry for Mercurial-specific
/// code.
pub trait Node: Sized {
    type Content;

    type GetParents: Stream<Item = Self, Error = Error>;
    type GetContent: Future<Item = Self::Content, Error = Error>;

    fn get_parents(&self) -> Self::GetParents;
    fn get_content(&self) -> Self::GetContent;
}


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

//! A hash of a node (changeset, manifest or file).

use std::fmt;
use std::fmt::Display;
use std::result;
use std::str::FromStr;

use abomonation_derive::Abomonation;
use anyhow::Result;
use ascii::AsciiStr;
use ascii::AsciiString;
use edenapi_types::CommitId as EdenapiCommitId;
use mononoke_types::sha1_hash;
use mononoke_types::sha1_hash::Sha1;
use mononoke_types::sha1_hash::Sha1Prefix;
use mononoke_types::FileType;
use quickcheck_arbitrary_derive::Arbitrary;
use sql::mysql;
/// Type used to represent a node hash in the Mercurial client's Rust code.
/// Equivalent to HgNodeHash;
use types::HgId;

use crate::manifest::Type;
use crate::thrift;
use crate::RepoPath;

pub const NULL_HASH: HgNodeHash = HgNodeHash(sha1_hash::NULL);
pub const NULL_CSID: HgChangesetId = HgChangesetId(NULL_HASH);

/// This structure represents Sha1 based hashes that are used in Mercurial, but the Sha1
/// structure is private outside this crate to keep it an implementation detail.
/// This is why the main constructors to create this structure are from_bytes and from_ascii_str
/// which parses raw bytes or hex string to create HgNodeHash.
#[derive(Arbitrary, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)]
#[derive(Abomonation)]
pub struct HgNodeHash(pub(crate) Sha1);

impl HgNodeHash {
    pub const fn new(sha1: Sha1) -> Self {
        HgNodeHash(sha1)
    }

    pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
        Sha1::from_bytes(bytes).map(HgNodeHash)
    }

    pub fn from_thrift(thrift_hash: thrift::HgNodeHash) -> Result<Self> {
        Ok(HgNodeHash(Sha1::from_thrift(thrift_hash.0)?))
    }

    pub fn from_thrift_opt(thrift_hash_opt: Option<thrift::HgNodeHash>) -> Result<Option<Self>> {
        match thrift_hash_opt {
            Some(h) => Ok(Some(Self::from_thrift(h)?)),
            None => Ok(None),
        }
    }

    pub fn as_bytes(&self) -> &[u8] {
        self.0.as_ref()
    }

    pub fn from_static_str(hash: &'static str) -> Result<Self> {
        Sha1::from_str(hash).map(HgNodeHash)
    }

    pub fn sha1(&self) -> &Sha1 {
        &self.0
    }

    #[inline]
    pub fn from_ascii_str(s: &AsciiStr) -> Result<Self> {
        Sha1::from_ascii_str(s).map(HgNodeHash)
    }

    /// Returns a 40 hex digits representation of the sha1 hash
    #[inline]
    pub fn to_hex(&self) -> AsciiString {
        self.0.to_hex()
    }

    #[inline]
    pub fn into_option(self) -> Option<Self> {
        if self == NULL_HASH { None } else { Some(self) }
    }

    pub fn into_thrift(self) -> thrift::HgNodeHash {
        thrift::HgNodeHash(self.0.into_thrift())
    }

    #[inline]
    pub fn display_opt<'a>(opt_hash: Option<&'a HgNodeHash>) -> OptDisplay<'a, Self> {
        OptDisplay { inner: opt_hash }
    }

    /// Return a stable hash fingerprint that can be used for sampling
    #[inline]
    pub fn sampling_fingerprint(&self) -> u64 {
        let byte_slice = &self.0.as_ref();
        let mut bytes: [u8; 8] = [0; 8];
        bytes.copy_from_slice(&byte_slice[0..8]);
        u64::from_le_bytes(bytes)
    }
}

pub struct OptDisplay<'a, T> {
    inner: Option<&'a T>,
}

impl<'a, T: Display> Display for OptDisplay<'a, T> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match self.inner {
            Some(inner) => inner.fmt(fmt),
            None => write!(fmt, "(none)"),
        }
    }
}

impl From<Option<HgNodeHash>> for HgNodeHash {
    fn from(h: Option<HgNodeHash>) -> Self {
        match h {
            None => NULL_HASH,
            Some(h) => h,
        }
    }
}

impl From<HgNodeHash> for HgId {
    fn from(node: HgNodeHash) -> Self {
        HgId::from_byte_array(node.0.into_byte_array())
    }
}

impl From<HgId> for HgNodeHash {
    fn from(hgid: HgId) -> Self {
        Self::from_bytes(hgid.as_ref()).unwrap()
    }
}

struct StringVisitor;

impl<'de> serde::de::Visitor<'de> for StringVisitor {
    type Value = String;

    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter.write_str("40 hex digits")
    }

    fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
    where
        E: serde::de::Error,
    {
        Ok(value.to_string())
    }

    fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
    where
        E: serde::de::Error,
    {
        Ok(value)
    }
}

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

impl<'de> serde::de::Deserialize<'de> for HgNodeHash {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::de::Deserializer<'de>,
    {
        let hex = deserializer.deserialize_string(StringVisitor)?;
        match Sha1::from_str(hex.as_str()) {
            Ok(sha1) => Ok(HgNodeHash(sha1)),
            Err(error) => Err(serde::de::Error::custom(error)),
        }
    }
}

impl AsRef<[u8]> for HgNodeHash {
    fn as_ref(&self) -> &[u8] {
        self.0.as_ref()
    }
}

impl FromStr for HgNodeHash {
    type Err = <Sha1 as FromStr>::Err;

    fn from_str(s: &str) -> result::Result<Self, Self::Err> {
        Sha1::from_str(s).map(HgNodeHash)
    }
}

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

#[derive(Arbitrary, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)]
#[derive(Abomonation, mysql::OptTryFromRowField)]
pub struct HgChangesetId(HgNodeHash);

impl HgChangesetId {
    #[inline]
    pub fn from_ascii_str(s: &AsciiStr) -> Result<HgChangesetId> {
        HgNodeHash::from_ascii_str(s).map(HgChangesetId)
    }

    pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
        HgNodeHash::from_bytes(bytes).map(HgChangesetId)
    }

    pub fn from_thrift(thrift_hash: thrift::HgNodeHash) -> Result<Self> {
        HgNodeHash::from_thrift(thrift_hash).map(HgChangesetId)
    }

    pub fn from_thrift_opt(thrift_hash_opt: Option<thrift::HgNodeHash>) -> Result<Option<Self>> {
        match thrift_hash_opt {
            Some(h) => Ok(Some(Self::from_thrift(h)?)),
            None => Ok(None),
        }
    }

    pub fn as_bytes(&self) -> &[u8] {
        self.0.as_bytes()
    }

    pub fn into_nodehash(self) -> HgNodeHash {
        self.0
    }

    pub fn into_thrift(self) -> thrift::HgNodeHash {
        self.into_nodehash().into_thrift()
    }

    pub const fn new(hash: HgNodeHash) -> Self {
        HgChangesetId(hash)
    }

    #[inline]
    pub fn to_hex(&self) -> AsciiString {
        self.0.to_hex()
    }

    /// Produce a key suitable for using in a blobstore.
    #[inline]
    pub fn blobstore_key(&self) -> String {
        format!("hgchangeset.sha1.{}", self.0)
    }

    #[inline]
    pub fn display_opt<'a>(opt_changeset_id: Option<&'a HgChangesetId>) -> OptDisplay<'a, Self> {
        OptDisplay {
            inner: opt_changeset_id,
        }
    }

    #[inline]
    pub fn sampling_fingerprint(&self) -> u64 {
        self.0.sampling_fingerprint()
    }
}

impl AsRef<[u8]> for HgChangesetId {
    fn as_ref(&self) -> &[u8] {
        self.0.as_ref()
    }
}

impl FromStr for HgChangesetId {
    type Err = <HgNodeHash as FromStr>::Err;

    fn from_str(s: &str) -> result::Result<HgChangesetId, Self::Err> {
        HgNodeHash::from_str(s).map(HgChangesetId)
    }
}

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

impl From<HgChangesetId> for EdenapiCommitId {
    fn from(value: HgChangesetId) -> Self {
        EdenapiCommitId::Hg(value.into())
    }
}

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

impl<'de> serde::de::Deserialize<'de> for HgChangesetId {
    fn deserialize<D>(deserializer: D) -> Result<HgChangesetId, D::Error>
    where
        D: serde::de::Deserializer<'de>,
    {
        let hex = deserializer.deserialize_string(StringVisitor)?;
        match HgNodeHash::from_str(hex.as_str()) {
            Ok(hash) => Ok(HgChangesetId::new(hash)),
            Err(error) => Err(serde::de::Error::custom(error)),
        }
    }
}

impl From<HgId> for HgChangesetId {
    fn from(hgid: HgId) -> Self {
        HgChangesetId::new(HgNodeHash::from(hgid))
    }
}

impl From<HgChangesetId> for HgId {
    fn from(hg_cs_id: HgChangesetId) -> HgId {
        hg_cs_id.into_nodehash().into()
    }
}

/// An identifier for a changeset hash prefix in Nercurial.
#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)]
#[derive(Abomonation)]
pub struct HgChangesetIdPrefix(Sha1Prefix);

impl HgChangesetIdPrefix {
    pub const fn new(sha1prefix: Sha1Prefix) -> Self {
        HgChangesetIdPrefix(sha1prefix)
    }

    pub fn from_bytes<B: AsRef<[u8]> + ?Sized>(bytes: &B) -> Result<Self> {
        Sha1Prefix::from_bytes(bytes).map(Self::new)
    }

    #[inline]
    pub fn min_cs(&self) -> HgChangesetId {
        HgChangesetId::new(
            HgNodeHash::from_bytes(self.0.min_as_ref()).expect("Min sha1 is a valid sha1"),
        )
    }

    #[inline]
    pub fn max_cs(&self) -> HgChangesetId {
        HgChangesetId::new(
            HgNodeHash::from_bytes(self.0.max_as_ref()).expect("Max sha1 is a valid sha1"),
        )
    }

    #[inline]
    pub fn min_as_ref(&self) -> &[u8] {
        self.0.min_as_ref()
    }

    #[inline]
    pub fn max_as_ref(&self) -> &[u8] {
        self.0.max_as_ref()
    }

    #[inline]
    pub fn into_hg_changeset_id(self) -> Option<HgChangesetId> {
        self.0.into_sha1().map(HgNodeHash).map(HgChangesetId)
    }
}

impl FromStr for HgChangesetIdPrefix {
    type Err = <Sha1Prefix as FromStr>::Err;
    fn from_str(s: &str) -> result::Result<HgChangesetIdPrefix, Self::Err> {
        Sha1Prefix::from_str(s).map(HgChangesetIdPrefix)
    }
}

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

#[derive(Debug, Clone, Eq, PartialEq, Hash)]
/// The type for resolving changesets by prefix of the hash
pub enum HgChangesetIdsResolvedFromPrefix {
    /// Found single changeset
    Single(HgChangesetId),
    /// Found several changesets within the limit provided
    Multiple(Vec<HgChangesetId>),
    /// Found too many changesets exceeding the limit provided
    TooMany(Vec<HgChangesetId>),
    /// Changeset was not found
    NoMatch,
}

#[derive(Arbitrary, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)]
pub struct HgManifestId(HgNodeHash);

impl HgManifestId {
    pub fn into_nodehash(self) -> HgNodeHash {
        self.0
    }

    pub const fn new(hash: HgNodeHash) -> Self {
        HgManifestId(hash)
    }

    pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
        HgNodeHash::from_bytes(bytes).map(HgManifestId)
    }

    pub fn as_bytes(&self) -> &[u8] {
        self.0.as_bytes()
    }

    #[inline]
    pub fn to_hex(&self) -> AsciiString {
        self.0.to_hex()
    }

    /// Produce a key suitable for using in a blobstore.
    #[inline]
    pub fn blobstore_key(&self) -> String {
        format!("hgmanifest.sha1.{}", self.0)
    }

    #[inline]
    pub fn sampling_fingerprint(&self) -> u64 {
        self.0.sampling_fingerprint()
    }
}

impl FromStr for HgManifestId {
    type Err = <HgNodeHash as FromStr>::Err;

    fn from_str(s: &str) -> result::Result<HgManifestId, Self::Err> {
        HgNodeHash::from_str(s).map(HgManifestId)
    }
}

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

#[derive(Arbitrary, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)]
#[derive(Abomonation, mysql::OptTryFromRowField)]
pub struct HgFileNodeId(HgNodeHash);

impl HgFileNodeId {
    pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
        HgNodeHash::from_bytes(bytes).map(HgFileNodeId)
    }

    pub fn from_thrift(thrift_hash: thrift::HgNodeHash) -> Result<Self> {
        HgNodeHash::from_thrift(thrift_hash).map(HgFileNodeId)
    }

    pub fn from_thrift_opt(thrift_hash_opt: Option<thrift::HgNodeHash>) -> Result<Option<Self>> {
        match thrift_hash_opt {
            Some(h) => Ok(Some(Self::from_thrift(h)?)),
            None => Ok(None),
        }
    }

    pub fn as_bytes(&self) -> &[u8] {
        self.0.as_bytes()
    }

    pub fn into_nodehash(self) -> HgNodeHash {
        self.0
    }

    pub const fn new(hash: HgNodeHash) -> Self {
        HgFileNodeId(hash)
    }

    #[inline]
    pub fn to_hex(&self) -> AsciiString {
        self.0.to_hex()
    }

    /// Produce a key suitable for using in a blobstore.
    #[inline]
    pub fn blobstore_key(&self) -> String {
        format!("hgfilenode.sha1.{}", self.0)
    }

    #[inline]
    pub fn sampling_fingerprint(&self) -> u64 {
        self.0.sampling_fingerprint()
    }
}

impl FromStr for HgFileNodeId {
    type Err = <HgNodeHash as FromStr>::Err;

    fn from_str(s: &str) -> result::Result<HgFileNodeId, Self::Err> {
        HgNodeHash::from_str(s).map(HgFileNodeId)
    }
}

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

#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)]
pub enum HgEntryId {
    File(FileType, HgFileNodeId),
    Manifest(HgManifestId),
}

impl HgEntryId {
    pub fn into_nodehash(self) -> HgNodeHash {
        match self {
            HgEntryId::File(_, file_hash) => file_hash.into_nodehash(),
            HgEntryId::Manifest(manifest_hash) => manifest_hash.into_nodehash(),
        }
    }

    #[inline]
    pub fn to_hex(&self) -> AsciiString {
        match self {
            HgEntryId::File(_, filenode_id) => filenode_id.to_hex(),
            HgEntryId::Manifest(manifest_id) => manifest_id.to_hex(),
        }
    }

    #[inline]
    pub fn to_filenode(&self) -> Option<(FileType, HgFileNodeId)> {
        match self {
            HgEntryId::File(file_type, filenode_id) => Some((*file_type, *filenode_id)),
            _ => None,
        }
    }

    #[inline]
    pub fn to_manifest(&self) -> Option<HgManifestId> {
        match self {
            HgEntryId::Manifest(manifest_id) => Some(*manifest_id),
            _ => None,
        }
    }

    #[inline]
    pub fn get_type(&self) -> Type {
        match self {
            HgEntryId::File(file_type, _) => Type::File(*file_type),
            HgEntryId::Manifest(_) => Type::Tree,
        }
    }
}

impl From<HgManifestId> for HgEntryId {
    fn from(manifest_id: HgManifestId) -> Self {
        HgEntryId::Manifest(manifest_id)
    }
}

impl Display for HgEntryId {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        (*self).into_nodehash().fmt(fmt)
    }
}

/// A (path, hash) combination. This is the key used throughout Mercurial for manifest and file
/// nodes.
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct HgNodeKey {
    pub path: RepoPath,
    pub hash: HgNodeHash,
}

impl Display for HgNodeKey {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "path: {}, hash: {}", self.path, self.hash)
    }
}

macro_rules! impl_hash {
    ($hash_type: ident) => {
        impl slog::Value for $hash_type {
            fn serialize(
                &self,
                _record: &slog::Record,
                key: slog::Key,
                serializer: &mut dyn slog::Serializer,
            ) -> slog::Result {
                let hex = self.to_hex();
                serializer.emit_str(key, hex.as_str())
            }
        }
    };
}

impl_hash!(HgNodeHash);
impl_hash!(HgChangesetId);
impl_hash!(HgManifestId);
impl_hash!(HgFileNodeId);
impl_hash!(HgEntryId);