<commit_before>use std::path::PathBuf;

use std::fmt::{Display, Debug, Formatter};
use std::fmt::Error as FmtError;
use std::result::Result as RResult;
use std::path::Components;

use libimagerror::into::IntoError;

use error::StoreErrorKind as SEK;
use store::Result;
use store::Store;

/// The Index into the Store
#[derive(Debug, Clone, PartialEq, Hash, Eq, PartialOrd, Ord)]
pub struct StoreId {
    base: Option<PathBuf>,
    id:   PathBuf,
}

impl StoreId {

    pub fn new(base: Option<PathBuf>, id: PathBuf) -> Result<StoreId> {
        StoreId::new_baseless(id).map(|mut sid| { sid.base = base; sid })
    }

    pub fn new_baseless(id: PathBuf) -> Result<StoreId> {
        if id.is_absolute() {
            Err(SEK::StoreIdLocalPartAbsoluteError.into_error())
        } else {
            Ok(StoreId {
                base: None,
                id: id
            })
        }
    }

    pub fn without_base(mut self) -> StoreId {
        self.base = None;
        self
    }

    pub fn with_base(mut self, base: PathBuf) -> Self {
        self.base = Some(base);
        self
    }

    pub fn storified(self, store: &Store) -> StoreId {
        StoreId {
            base: Some(store.path().clone()),
            id: self.id
        }
    }

    pub fn exists(&self) -> bool {
        let pb : PathBuf = self.clone().into();
        pb.exists()
    }

    pub fn is_file(&self) -> bool {
        true
    }

    pub fn is_dir(&self) -> bool {
        false
    }

    pub fn to_str(&self) -> Result<String> {
        if self.base.is_some() {
            let mut base = self.base.as_ref().cloned().unwrap();
            base.push(self.id.clone());
            base
        } else {
            self.id.clone()
        }
        .to_str()
        .map(String::from)
        .ok_or(SEK::StoreIdHandlingError.into_error())
    }

    /// Returns the components of the `id` part of the StoreId object.
    ///
    /// Can be used to check whether a StoreId points to an entry in a specific collection of
    /// StoreIds.
    pub fn components(&self) -> Components {
        self.id.components()
    }

    /// Convenience function over PathBuf::starts_with().
    ///
    /// This function calls `PathBuf::starts_with()` on the _local_ part of `self`.
    pub fn is_in_collection(&self, collspec: &PathBuf) -> bool {
        self.id.starts_with(collspec)
    }

}

impl Into<PathBuf> for StoreId {

    fn into(self) -> PathBuf {
        let mut base = self.base.unwrap_or(PathBuf::from("/"));
        base.push(self.id);
        base
    }

}

impl Display for StoreId {

    fn fmt(&self, fmt: &mut Formatter) -> RResult<(), FmtError> {
        match self.id.to_str() {
            Some(s) => write!(fmt, "{}", s),
            None    => write!(fmt, "{}", self.id.to_string_lossy()),
        }
    }

}

/// This Trait allows you to convert various representations to a single one
/// suitable for usage in the Store
pub trait IntoStoreId {
    fn into_storeid(self) -> Result<StoreId>;
}

impl IntoStoreId for StoreId {
    fn into_storeid(self) -> Result<StoreId> {
        Ok(self)
    }
}

impl IntoStoreId for PathBuf {
    fn into_storeid(self) -> Result<StoreId> {
        StoreId::new_baseless(self)
    }
}

#[macro_export]
macro_rules! module_entry_path_mod {
    ($name:expr) => (
        #[deny(missing_docs,
                missing_copy_implementations,
                trivial_casts, trivial_numeric_casts,
                unsafe_code,
                unstable_features,
                unused_import_braces, unused_qualifications,
                unused_imports)]
        /// A helper module to create valid module entry paths
        pub mod module_path {
            use std::convert::AsRef;
            use std::path::Path;
            use std::path::PathBuf;

            use $crate::storeid::StoreId;
            use $crate::store::Result;

            /// A Struct giving you the ability to choose store entries assigned
            /// to it.
            ///
            /// It is created through a call to `new`.
            pub struct ModuleEntryPath(PathBuf);

            impl ModuleEntryPath {
                /// Path has to be a valid UTF-8 string or this will panic!
                pub fn new<P: AsRef<Path>>(pa: P) -> ModuleEntryPath {
                    let mut path = PathBuf::new();
                    path.push(format!("{}", $name));
                    path.push(pa.as_ref().clone());
                    let name = pa.as_ref().file_name().unwrap()
                        .to_str().unwrap();
                    path.set_file_name(name);
                    ModuleEntryPath(path)
                }
            }

            impl $crate::storeid::IntoStoreId for ModuleEntryPath {
                fn into_storeid(self) -> Result<$crate::storeid::StoreId> {
                    StoreId::new(None, self.0)
                }
            }
        }
    )
}

pub struct StoreIdIterator {
    iter: Box<Iterator<Item = StoreId>>,
}

impl Debug for StoreIdIterator {

    fn fmt(&self, fmt: &mut Formatter) -> RResult<(), FmtError> {
        write!(fmt, "StoreIdIterator")
    }

}

impl StoreIdIterator {

    pub fn new(iter: Box<Iterator<Item = StoreId>>) -> StoreIdIterator {
        StoreIdIterator {
            iter: iter,
        }
    }

}

impl Iterator for StoreIdIterator {
    type Item = StoreId;

    fn next(&mut self) -> Option<StoreId> {
        self.iter.next()
    }

}

#[cfg(test)]
mod test {

    use storeid::IntoStoreId;

    module_entry_path_mod!("test");

    #[test]
    fn correct_path() {
        let p = module_path::ModuleEntryPath::new("test");

        assert_eq!(p.into_storeid().unwrap().to_str().unwrap(), "test/test");
    }

    #[test]
    fn storeid_in_collection() {
        let p = module_path::ModuleEntryPath::new("1/2/3/4/5/6/7/8/9/0").into_storeid().unwrap();

        assert!(p.is_in_collection(&["test", "1"]));
        assert!(p.is_in_collection(&["test", "1", "2"]));
        assert!(p.is_in_collection(&["test", "1", "2", "3"]));
        assert!(p.is_in_collection(&["test", "1", "2", "3", "4"]));
        assert!(p.is_in_collection(&["test", "1", "2", "3", "4", "5"]));
        assert!(p.is_in_collection(&["test", "1", "2", "3", "4", "5", "6"]));
        assert!(p.is_in_collection(&["test", "1", "2", "3", "4", "5", "6", "7"]));
        assert!(p.is_in_collection(&["test", "1", "2", "3", "4", "5", "6", "7", "8"]));
        assert!(p.is_in_collection(&["test", "1", "2", "3", "4", "5", "6", "7", "8", "9"]));

        // "0" is the filename, not a collection
        assert!(!p.is_in_collection(&["test", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"]));

        assert!(!p.is_in_collection(&["test", "0", "2", "3", "4", "5", "6", "7", "8", "9", "0"]));
        assert!(!p.is_in_collection(&["test", "1", "2", "3", "4", "5", "6", "8"]));
        assert!(!p.is_in_collection(&["test", "1", "2", "3", "leet", "5", "6", "7"]));
    }

}
<commit_msg>Adapt tests for new StoreId::is_in_collection() interface<commit_after>use std::path::PathBuf;

use std::fmt::{Display, Debug, Formatter};
use std::fmt::Error as FmtError;
use std::result::Result as RResult;
use std::path::Components;

use libimagerror::into::IntoError;

use error::StoreErrorKind as SEK;
use store::Result;
use store::Store;

/// The Index into the Store
#[derive(Debug, Clone, PartialEq, Hash, Eq, PartialOrd, Ord)]
pub struct StoreId {
    base: Option<PathBuf>,
    id:   PathBuf,
}

impl StoreId {

    pub fn new(base: Option<PathBuf>, id: PathBuf) -> Result<StoreId> {
        StoreId::new_baseless(id).map(|mut sid| { sid.base = base; sid })
    }

    pub fn new_baseless(id: PathBuf) -> Result<StoreId> {
        if id.is_absolute() {
            Err(SEK::StoreIdLocalPartAbsoluteError.into_error())
        } else {
            Ok(StoreId {
                base: None,
                id: id
            })
        }
    }

    pub fn without_base(mut self) -> StoreId {
        self.base = None;
        self
    }

    pub fn with_base(mut self, base: PathBuf) -> Self {
        self.base = Some(base);
        self
    }

    pub fn storified(self, store: &Store) -> StoreId {
        StoreId {
            base: Some(store.path().clone()),
            id: self.id
        }
    }

    pub fn exists(&self) -> bool {
        let pb : PathBuf = self.clone().into();
        pb.exists()
    }

    pub fn is_file(&self) -> bool {
        true
    }

    pub fn is_dir(&self) -> bool {
        false
    }

    pub fn to_str(&self) -> Result<String> {
        if self.base.is_some() {
            let mut base = self.base.as_ref().cloned().unwrap();
            base.push(self.id.clone());
            base
        } else {
            self.id.clone()
        }
        .to_str()
        .map(String::from)
        .ok_or(SEK::StoreIdHandlingError.into_error())
    }

    /// Returns the components of the `id` part of the StoreId object.
    ///
    /// Can be used to check whether a StoreId points to an entry in a specific collection of
    /// StoreIds.
    pub fn components(&self) -> Components {
        self.id.components()
    }

    /// Convenience function over PathBuf::starts_with().
    ///
    /// This function calls `PathBuf::starts_with()` on the _local_ part of `self`.
    pub fn is_in_collection(&self, collspec: &PathBuf) -> bool {
        self.id.starts_with(collspec)
    }

}

impl Into<PathBuf> for StoreId {

    fn into(self) -> PathBuf {
        let mut base = self.base.unwrap_or(PathBuf::from("/"));
        base.push(self.id);
        base
    }

}

impl Display for StoreId {

    fn fmt(&self, fmt: &mut Formatter) -> RResult<(), FmtError> {
        match self.id.to_str() {
            Some(s) => write!(fmt, "{}", s),
            None    => write!(fmt, "{}", self.id.to_string_lossy()),
        }
    }

}

/// This Trait allows you to convert various representations to a single one
/// suitable for usage in the Store
pub trait IntoStoreId {
    fn into_storeid(self) -> Result<StoreId>;
}

impl IntoStoreId for StoreId {
    fn into_storeid(self) -> Result<StoreId> {
        Ok(self)
    }
}

impl IntoStoreId for PathBuf {
    fn into_storeid(self) -> Result<StoreId> {
        StoreId::new_baseless(self)
    }
}

#[macro_export]
macro_rules! module_entry_path_mod {
    ($name:expr) => (
        #[deny(missing_docs,
                missing_copy_implementations,
                trivial_casts, trivial_numeric_casts,
                unsafe_code,
                unstable_features,
                unused_import_braces, unused_qualifications,
                unused_imports)]
        /// A helper module to create valid module entry paths
        pub mod module_path {
            use std::convert::AsRef;
            use std::path::Path;
            use std::path::PathBuf;

            use $crate::storeid::StoreId;
            use $crate::store::Result;

            /// A Struct giving you the ability to choose store entries assigned
            /// to it.
            ///
            /// It is created through a call to `new`.
            pub struct ModuleEntryPath(PathBuf);

            impl ModuleEntryPath {
                /// Path has to be a valid UTF-8 string or this will panic!
                pub fn new<P: AsRef<Path>>(pa: P) -> ModuleEntryPath {
                    let mut path = PathBuf::new();
                    path.push(format!("{}", $name));
                    path.push(pa.as_ref().clone());
                    let name = pa.as_ref().file_name().unwrap()
                        .to_str().unwrap();
                    path.set_file_name(name);
                    ModuleEntryPath(path)
                }
            }

            impl $crate::storeid::IntoStoreId for ModuleEntryPath {
                fn into_storeid(self) -> Result<$crate::storeid::StoreId> {
                    StoreId::new(None, self.0)
                }
            }
        }
    )
}

pub struct StoreIdIterator {
    iter: Box<Iterator<Item = StoreId>>,
}

impl Debug for StoreIdIterator {

    fn fmt(&self, fmt: &mut Formatter) -> RResult<(), FmtError> {
        write!(fmt, "StoreIdIterator")
    }

}

impl StoreIdIterator {

    pub fn new(iter: Box<Iterator<Item = StoreId>>) -> StoreIdIterator {
        StoreIdIterator {
            iter: iter,
        }
    }

}

impl Iterator for StoreIdIterator {
    type Item = StoreId;

    fn next(&mut self) -> Option<StoreId> {
        self.iter.next()
    }

}

#[cfg(test)]
mod test {

    use storeid::IntoStoreId;

    module_entry_path_mod!("test");

    #[test]
    fn correct_path() {
        let p = module_path::ModuleEntryPath::new("test");

        assert_eq!(p.into_storeid().unwrap().to_str().unwrap(), "test/test");
    }

    #[test]
    fn storeid_in_collection() {
        use std::path::PathBuf;
        let p = module_path::ModuleEntryPath::new("1/2/3/4/5/6/7/8/9/0").into_storeid().unwrap();

        assert!(p.is_in_collection(&PathBuf::from("test/1")));
        assert!(p.is_in_collection(&PathBuf::from("test/1/2")));
        assert!(p.is_in_collection(&PathBuf::from("test/1/2/3")));
        assert!(p.is_in_collection(&PathBuf::from("test/1/2/3/4")));
        assert!(p.is_in_collection(&PathBuf::from("test/1/2/3/4/5")));
        assert!(p.is_in_collection(&PathBuf::from("test/1/2/3/4/5/6")));
        assert!(p.is_in_collection(&PathBuf::from("test/1/2/3/4/5/6/7")));
        assert!(p.is_in_collection(&PathBuf::from("test/1/2/3/4/5/6/7/8")));
        assert!(p.is_in_collection(&PathBuf::from("test/1/2/3/4/5/6/7/8/9")));
        assert!(p.is_in_collection(&PathBuf::from("test/1/2/3/4/5/6/7/8/9/0")));

        assert!(!p.is_in_collection(&PathBuf::from("test/0/2/3/4/5/6/7/8/9/0")));
        assert!(!p.is_in_collection(&PathBuf::from("test/1/2/3/4/5/6/8")));
        assert!(!p.is_in_collection(&PathBuf::from("test/1/2/3/leet/5/6/7")));
    }

}
<|endoftext|>


<commit_before>//! Common code of `build.rs` of two tests

pub use protobuf_codegen::Customize;

use std::io::Write;
use std::io::Read;
use std::io::BufRead;
use std::io::BufReader;
use std::fs;
use std::fmt;
use std::path::Path;

use glob;

pub fn glob_simple(pattern: &str) -> Vec<String> {
    let mut r: Vec<_> = glob::glob(pattern)
        .expect("glob")
        .map(|g| {
            g.expect("item")
                .as_path()
                .to_str()
                .expect("utf-8")
                .to_owned()
        })
        .collect();
    // Make test stable
    r.sort();
    r
}

fn read_gitignore(dir: &Path) -> Vec<String> {
    let mut patterns = Vec::new();

    let gitignore = format!("{}/.gitignore", dir.display());
    let gitignore = &Path::new(&gitignore);
    if gitignore.exists() {
        let gitignore = fs::File::open(gitignore).expect("open gitignore");
        for line in BufReader::new(gitignore).lines() {
            let line = line.expect("read_line");
            if line.is_empty() || line.starts_with("#") {
                continue;
            }
            patterns.push(line);
        }
    }

    patterns
}

fn clean_recursively(dir: &Path, patterns: &[&str]) {
    assert!(dir.is_dir());

    let gitignore_patterns = read_gitignore(dir);

    let mut patterns = patterns.to_vec();
    patterns.extend(gitignore_patterns.iter().map(String::as_str));

    let patterns_compiled: Vec<_> = patterns.iter()
        .map(|&p| glob::Pattern::new(p).expect("failed to compile pattern"))
        .collect();

    for entry in fs::read_dir(dir).expect("read_dir") {
        let entry = entry.expect("entry");
        let entry_path = entry.path();
        let file_name = entry_path.as_path().file_name().unwrap().to_str().unwrap();
        if entry.metadata().expect("metadata").is_dir() {
            clean_recursively(&entry_path, &patterns);
        } else if file_name == ".gitignore" {
            // keep it
        } else {
            for pattern in &patterns_compiled {
                if pattern.matches(file_name) {
                    fs::remove_file(&entry_path).expect("remove_file");
                    break;
                }
            }
        }
    }
}

pub fn clean_old_files() {
    clean_recursively(&Path::new("src"), &["*_pb.rs", "*_pb_proto3.rs"]);
}

#[derive(Default)]
pub struct GenInDirArgs<'a> {
    pub out_dir: &'a str,
    pub input: &'a [&'a str],
    pub includes: &'a [&'a str],
    pub customize: Customize,
}

/// Generate mod.rs from all files in a directory
pub fn gen_mod_rs_in_dir(dir: &str) {
    assert!(Path::new(dir).is_dir());

    let mut mod_rs = fs::File::create(&format!("{}/mod.rs", dir)).expect("create");

    writeln!(mod_rs, "// generated by {}", module_path!()).expect("write");
    writeln!(mod_rs, "").expect("write");

    let rs_files = glob_simple(&format!("{}/*.rs", dir));

    for rs in rs_files {
        let file_name = Path::new(&rs).file_name().expect("file_name").to_str().expect("file_name");
        if file_name == "mod.rs" {
            continue;
        }
        assert!(file_name.ends_with(".rs"));
        let mod_name = &file_name[..file_name.len() - ".rs".len()];

        if mod_name.contains("carllerche") {
            writeln!(mod_rs, r#"#[cfg(feature = "with-bytes")]"#).expect("write");
        }
        writeln!(mod_rs, "mod {};", mod_name).expect("write");
    }

    mod_rs.flush().expect("flush");
}

enum TestProtobufVersions {
    V2,
    V3,
    Google,
}

#[derive(Eq, PartialEq, Debug)]
enum ProtobufSyntax {
    V2,
    V3,
}

fn test_version_from_file_path(mut file_path: &Path) -> TestProtobufVersions {
    loop {
        let name = file_path.file_name().expect("file_name").to_str().expect("to_str");
        if name == "v2" {
            return TestProtobufVersions::V2;
        } else if name == "v3" {
            return TestProtobufVersions::V3;
        } else if name == "google" {
            return TestProtobufVersions::Google;
        }

        file_path = file_path.parent().expect("parent");
    }
}

fn test_version_from_file_content(file_path: &Path) -> ProtobufSyntax {
    let content = fs::read_to_string(file_path).expect("read_to_string");
    if content.contains("syntax = \"proto2\"") {
        return ProtobufSyntax::V2;
    } else if content.contains("syntax = \"proto3\"") {
        return ProtobufSyntax::V3;
    } else {
        panic!("cannot detect protobuf version from file content: {:?}", file_path);
    }
}

fn check_test_version(file_path: &Path) {
    let expected_version_from_file_name = match test_version_from_file_path(file_path) {
        TestProtobufVersions::V2 => ProtobufSyntax::V2,
        TestProtobufVersions::V3 => ProtobufSyntax::V3,
        TestProtobufVersions::Google => return,
    };

    let version = test_version_from_file_content(file_path);
    assert_eq!(expected_version_from_file_name, version, "for file: {:?}", file_path);
}

pub fn gen_in_dir_impl<F, E>(dir: &str, include_dir: &str, gen: F)
    where
        F : for<'a> Fn(GenInDirArgs<'a>) -> Result<(), E>,
        E : fmt::Debug,
{
    info!("generating protos in {}", dir);

    let mut protos = Vec::new();
    for suffix in &[".proto", ".proto3"] {
        protos.extend(glob_simple(&format!("{}/*{}", dir, suffix)));
    }

    for file_path in &protos {
        check_test_version(Path::new(file_path));
    }

    assert!(!protos.is_empty(), "no protos found in {}", dir);

    let includes = ["../proto", include_dir];

    eprintln!("invoking protobubf compiler: out_dir: {:?}, input: {:?}, includes: {:?}",
        dir, protos, includes);

    gen(GenInDirArgs {
        out_dir: dir,
        input: &protos.iter().map(|a| a.as_ref()).collect::<Vec<&str>>(),
        includes: &includes,
        .. Default::default()
    }).expect("codegen failed");

    gen_mod_rs_in_dir(dir);
}

pub fn list_tests_in_dir(dir: &str) -> Vec<String> {
    let mut r = Vec::new();
    for entry in fs::read_dir(dir).expect("read_dir") {
        let entry = entry.expect("entry");
        let entry_path = entry.path();
        let file_name = entry_path.as_path().file_name().unwrap().to_str().unwrap();

        // temporart files
        if file_name.ends_with(".") {
            continue;
        }

        if !file_name.ends_with(".rs") || file_name.ends_with("_pb.rs") {
            continue;
        }

        if file_name == "mod.rs" {
            continue;
        }

        r.push(file_name[..file_name.len() - ".rs".len()].to_owned());
    }

    // Make test stable
    r.sort();

    r
}

pub fn copy_tests_v2_v3(v2_dir: &str, v3_dir: &str) {
    for test_name in list_tests_in_dir(v2_dir) {
        let mut p2f = fs::File::open(&format!("{}/{}_pb.proto", v2_dir, test_name))
            .expect("open v2 .proto");
        let mut proto = String::new();
        p2f.read_to_string(&mut proto).expect("read .proto");
        drop(p2f);

        let mut r2f = fs::File::open(&format!("{}/{}.rs", v2_dir, test_name))
            .expect("open v2 .rs");
        let mut rs = String::new();
        r2f.read_to_string(&mut rs).expect("read .rs");
        drop(r2f);

        let mut p3f = fs::File::create(&format!("{}/{}_pb.proto", v3_dir, test_name))
            .expect("create v3 .proto");
        let mut r3f = fs::File::create(&format!("{}/{}.rs", v3_dir, test_name))
            .expect("create v3 .rs");

        // convert proto2 to proto3
        let proto = proto.replace("optional ", "");
        let proto = proto.replace("required ", "");
        let proto = proto.replace("syntax = \"proto2\";", "syntax = \"proto3\";");
        write!(p3f, "// generated\n").expect("write");
        write!(p3f, "{}", proto).expect("write");
        p3f.flush().expect("flush");

        write!(r3f, "// generated\n").expect("write");
        write!(r3f, "{}", rs).expect("write");
        r3f.flush().expect("flush");
    }
}
<commit_msg>Better diag reporting<commit_after>//! Common code of `build.rs` of two tests

pub use protobuf_codegen::Customize;

use std::io::Write;
use std::io::Read;
use std::io::BufRead;
use std::io::BufReader;
use std::fs;
use std::fmt;
use std::path::Path;

use glob;

pub fn glob_simple(pattern: &str) -> Vec<String> {
    let mut r: Vec<_> = glob::glob(pattern)
        .expect("glob")
        .map(|g| {
            g.expect("item")
                .as_path()
                .to_str()
                .expect("utf-8")
                .to_owned()
        })
        .collect();
    // Make test stable
    r.sort();
    r
}

fn read_gitignore(dir: &Path) -> Vec<String> {
    let mut patterns = Vec::new();

    let gitignore = format!("{}/.gitignore", dir.display());
    let gitignore = &Path::new(&gitignore);
    if gitignore.exists() {
        let gitignore = fs::File::open(gitignore).expect("open gitignore");
        for line in BufReader::new(gitignore).lines() {
            let line = line.expect("read_line");
            if line.is_empty() || line.starts_with("#") {
                continue;
            }
            patterns.push(line);
        }
    }

    patterns
}

fn clean_recursively(dir: &Path, patterns: &[&str]) {
    assert!(dir.is_dir());

    let gitignore_patterns = read_gitignore(dir);

    let mut patterns = patterns.to_vec();
    patterns.extend(gitignore_patterns.iter().map(String::as_str));

    let patterns_compiled: Vec<_> = patterns.iter()
        .map(|&p| glob::Pattern::new(p).expect("failed to compile pattern"))
        .collect();

    for entry in fs::read_dir(dir).expect("read_dir") {
        let entry = entry.expect("entry");
        let entry_path = entry.path();
        let file_name = entry_path.as_path().file_name().unwrap().to_str().unwrap();
        if entry.metadata().expect("metadata").is_dir() {
            clean_recursively(&entry_path, &patterns);
        } else if file_name == ".gitignore" {
            // keep it
        } else {
            for pattern in &patterns_compiled {
                if pattern.matches(file_name) {
                    fs::remove_file(&entry_path).expect("remove_file");
                    break;
                }
            }
        }
    }
}

pub fn clean_old_files() {
    clean_recursively(&Path::new("src"), &["*_pb.rs", "*_pb_proto3.rs"]);
}

#[derive(Default)]
pub struct GenInDirArgs<'a> {
    pub out_dir: &'a str,
    pub input: &'a [&'a str],
    pub includes: &'a [&'a str],
    pub customize: Customize,
}

/// Generate mod.rs from all files in a directory
pub fn gen_mod_rs_in_dir(dir: &str) {
    assert!(Path::new(dir).is_dir());

    let mut mod_rs = fs::File::create(&format!("{}/mod.rs", dir)).expect("create");

    writeln!(mod_rs, "// generated by {}", module_path!()).expect("write");
    writeln!(mod_rs, "").expect("write");

    let rs_files = glob_simple(&format!("{}/*.rs", dir));

    for rs in rs_files {
        let file_name = Path::new(&rs).file_name().expect("file_name").to_str().expect("file_name");
        if file_name == "mod.rs" {
            continue;
        }
        assert!(file_name.ends_with(".rs"));
        let mod_name = &file_name[..file_name.len() - ".rs".len()];

        if mod_name.contains("carllerche") {
            writeln!(mod_rs, r#"#[cfg(feature = "with-bytes")]"#).expect("write");
        }
        writeln!(mod_rs, "mod {};", mod_name).expect("write");
    }

    mod_rs.flush().expect("flush");
}

enum TestProtobufVersions {
    V2,
    V3,
    Google,
}

#[derive(Eq, PartialEq, Debug)]
enum ProtobufSyntax {
    V2,
    V3,
}

fn test_version_from_file_path(mut file_path: &Path) -> TestProtobufVersions {
    loop {
        let name = file_path.file_name().expect("file_name").to_str().expect("to_str");
        if name == "v2" {
            return TestProtobufVersions::V2;
        } else if name == "v3" {
            return TestProtobufVersions::V3;
        } else if name == "google" {
            return TestProtobufVersions::Google;
        }

        file_path = file_path.parent().expect("parent");
    }
}

fn test_version_from_file_content(file_path: &Path) -> ProtobufSyntax {
    let content = fs::read_to_string(file_path)
        .expect(&format!("read_to_string {:?}", file_path));
    if content.contains("syntax = \"proto2\"") {
        return ProtobufSyntax::V2;
    } else if content.contains("syntax = \"proto3\"") {
        return ProtobufSyntax::V3;
    } else {
        panic!("cannot detect protobuf version from file content: {:?}", file_path);
    }
}

fn check_test_version(file_path: &Path) {
    let expected_version_from_file_name = match test_version_from_file_path(file_path) {
        TestProtobufVersions::V2 => ProtobufSyntax::V2,
        TestProtobufVersions::V3 => ProtobufSyntax::V3,
        TestProtobufVersions::Google => return,
    };

    let version = test_version_from_file_content(file_path);
    assert_eq!(expected_version_from_file_name, version, "for file: {:?}", file_path);
}

pub fn gen_in_dir_impl<F, E>(dir: &str, include_dir: &str, gen: F)
    where
        F : for<'a> Fn(GenInDirArgs<'a>) -> Result<(), E>,
        E : fmt::Debug,
{
    info!("generating protos in {}", dir);

    let mut protos = Vec::new();
    for suffix in &[".proto", ".proto3"] {
        protos.extend(glob_simple(&format!("{}/*{}", dir, suffix)));
    }

    for file_path in &protos {
        check_test_version(Path::new(file_path));
    }

    assert!(!protos.is_empty(), "no protos found in {}", dir);

    let includes = ["../proto", include_dir];

    eprintln!("invoking protobubf compiler: out_dir: {:?}, input: {:?}, includes: {:?}",
        dir, protos, includes);

    gen(GenInDirArgs {
        out_dir: dir,
        input: &protos.iter().map(|a| a.as_ref()).collect::<Vec<&str>>(),
        includes: &includes,
        .. Default::default()
    }).expect("codegen failed");

    gen_mod_rs_in_dir(dir);
}

pub fn list_tests_in_dir(dir: &str) -> Vec<String> {
    let mut r = Vec::new();
    for entry in fs::read_dir(dir).expect("read_dir") {
        let entry = entry.expect("entry");
        let entry_path = entry.path();
        let file_name = entry_path.as_path().file_name().unwrap().to_str().unwrap();

        // temporart files
        if file_name.ends_with(".") {
            continue;
        }

        if !file_name.ends_with(".rs") || file_name.ends_with("_pb.rs") {
            continue;
        }

        if file_name == "mod.rs" {
            continue;
        }

        r.push(file_name[..file_name.len() - ".rs".len()].to_owned());
    }

    // Make test stable
    r.sort();

    r
}

pub fn copy_tests_v2_v3(v2_dir: &str, v3_dir: &str) {
    for test_name in list_tests_in_dir(v2_dir) {
        let mut p2f = fs::File::open(&format!("{}/{}_pb.proto", v2_dir, test_name))
            .expect("open v2 .proto");
        let mut proto = String::new();
        p2f.read_to_string(&mut proto).expect("read .proto");
        drop(p2f);

        let mut r2f = fs::File::open(&format!("{}/{}.rs", v2_dir, test_name))
            .expect("open v2 .rs");
        let mut rs = String::new();
        r2f.read_to_string(&mut rs).expect("read .rs");
        drop(r2f);

        let mut p3f = fs::File::create(&format!("{}/{}_pb.proto", v3_dir, test_name))
            .expect("create v3 .proto");
        let mut r3f = fs::File::create(&format!("{}/{}.rs", v3_dir, test_name))
            .expect("create v3 .rs");

        // convert proto2 to proto3
        let proto = proto.replace("optional ", "");
        let proto = proto.replace("required ", "");
        let proto = proto.replace("syntax = \"proto2\";", "syntax = \"proto3\";");
        write!(p3f, "// generated\n").expect("write");
        write!(p3f, "{}", proto).expect("write");
        p3f.flush().expect("flush");

        write!(r3f, "// generated\n").expect("write");
        write!(r3f, "{}", rs).expect("write");
        r3f.flush().expect("flush");
    }
}
<|endoftext|>


<commit_before>//! Common code of `build.rs` of two tests

pub use protobuf_codegen::Customize;

use std::io::Write;
use std::io::Read;
use std::io::BufRead;
use std::io::BufReader;
use std::fs;
use std::fmt;
use std::path::Path;

use glob;

pub fn glob_simple(pattern: &str) -> Vec<String> {
    let mut r: Vec<_> = glob::glob(pattern)
        .expect("glob")
        .map(|g| {
            g.expect("item")
                .as_path()
                .to_str()
                .expect("utf-8")
                .to_owned()
        })
        .collect();
    // Make test stable
    r.sort();
    r
}

fn read_gitignore(dir: &Path) -> Vec<String> {
    let mut patterns = Vec::new();

    let gitignore = format!("{}/.gitignore", dir.display());
    let gitignore = &Path::new(&gitignore);
    if gitignore.exists() {
        let gitignore = fs::File::open(gitignore)
            .expect(&format!("open gitignore {:?}", gitignore));
        for line in BufReader::new(gitignore).lines() {
            let line = line.expect("read_line");
            if line.is_empty() || line.starts_with("#") {
                continue;
            }
            patterns.push(line);
        }
    }

    patterns
}

fn clean_recursively(dir: &Path, patterns: &[&str]) {
    assert!(dir.is_dir());

    eprintln!("cleaning ignored files in {:?}", dir);

    let gitignore_patterns = read_gitignore(dir);

    let mut patterns = patterns.to_vec();
    patterns.extend(gitignore_patterns.iter().map(String::as_str));

    let patterns_compiled: Vec<_> = patterns.iter()
        .map(|&p| glob::Pattern::new(p).expect("failed to compile pattern"))
        .collect();

    for entry in fs::read_dir(dir).expect("read_dir") {
        let entry = entry.expect("entry");
        let entry_path = entry.path();
        let file_name = entry_path.as_path().file_name().unwrap().to_str().unwrap();
        if entry.metadata().expect(&format!("metadata of {:?}", entry_path)).is_dir() {
            clean_recursively(&entry_path, &patterns);
        } else if file_name == ".gitignore" {
            // keep it
        } else {
            for pattern in &patterns_compiled {
                if pattern.matches(file_name) {
                    fs::remove_file(&entry_path).expect(&format!("remove_file {:?}", entry_path));
                    break;
                }
            }
        }
    }
}

pub fn clean_old_files() {
    clean_recursively(&Path::new("src"), &["*_pb.rs", "*_pb_proto3.rs"]);
}

#[derive(Default)]
pub struct GenInDirArgs<'a> {
    pub out_dir: &'a str,
    pub input: &'a [&'a str],
    pub includes: &'a [&'a str],
    pub customize: Customize,
}

/// Generate mod.rs from all files in a directory
pub fn gen_mod_rs_in_dir(dir: &str) {
    assert!(Path::new(dir).is_dir());

    let mut mod_rs = fs::File::create(&format!("{}/mod.rs", dir)).expect("create");

    writeln!(mod_rs, "// generated by {}", module_path!()).expect("write");
    writeln!(mod_rs, "").expect("write");

    let rs_files = glob_simple(&format!("{}/*.rs", dir));

    for rs in rs_files {
        let file_name = Path::new(&rs).file_name().expect("file_name").to_str().expect("file_name");
        if file_name == "mod.rs" {
            continue;
        }
        assert!(file_name.ends_with(".rs"));
        let mod_name = &file_name[..file_name.len() - ".rs".len()];

        if mod_name.contains("carllerche") {
            writeln!(mod_rs, r#"#[cfg(feature = "with-bytes")]"#).expect("write");
        }
        writeln!(mod_rs, "mod {};", mod_name).expect("write");
    }

    mod_rs.flush().expect("flush");
}

enum TestProtobufVersions {
    V2,
    V3,
    Google,
}

#[derive(Eq, PartialEq, Debug)]
enum ProtobufSyntax {
    V2,
    V3,
}

fn test_version_from_file_path(mut file_path: &Path) -> TestProtobufVersions {
    loop {
        let name = file_path.file_name().expect("file_name").to_str().expect("to_str");
        if name == "v2" {
            return TestProtobufVersions::V2;
        } else if name == "v3" {
            return TestProtobufVersions::V3;
        } else if name == "google" {
            return TestProtobufVersions::Google;
        }

        file_path = file_path.parent().expect("parent");
    }
}

fn test_version_from_file_content(file_path: &Path) -> ProtobufSyntax {
    let content = fs::read_to_string(file_path)
        .expect(&format!("read_to_string {:?}", file_path));
    if content.contains("syntax = \"proto2\"") {
        return ProtobufSyntax::V2;
    } else if content.contains("syntax = \"proto3\"") {
        return ProtobufSyntax::V3;
    } else {
        panic!("cannot detect protobuf version from file content: {:?}", file_path);
    }
}

fn check_test_version(file_path: &Path) {
    let expected_version_from_file_name = match test_version_from_file_path(file_path) {
        TestProtobufVersions::V2 => ProtobufSyntax::V2,
        TestProtobufVersions::V3 => ProtobufSyntax::V3,
        TestProtobufVersions::Google => return,
    };

    let version = test_version_from_file_content(file_path);
    assert_eq!(expected_version_from_file_name, version, "for file: {:?}", file_path);
}

pub fn gen_in_dir_impl<F, E>(dir: &str, include_dir: &str, gen: F)
    where
        F : for<'a> Fn(GenInDirArgs<'a>) -> Result<(), E>,
        E : fmt::Debug,
{
    info!("generating protos in {}", dir);

    let mut protos = Vec::new();
    for suffix in &[".proto", ".proto3"] {
        protos.extend(glob_simple(&format!("{}/*{}", dir, suffix)));
    }

    for file_path in &protos {
        check_test_version(Path::new(file_path));
    }

    assert!(!protos.is_empty(), "no protos found in {}", dir);

    let includes = ["../proto", include_dir];

    eprintln!("invoking protobubf compiler: out_dir: {:?}, input: {:?}, includes: {:?}",
        dir, protos, includes);

    gen(GenInDirArgs {
        out_dir: dir,
        input: &protos.iter().map(|a| a.as_ref()).collect::<Vec<&str>>(),
        includes: &includes,
        .. Default::default()
    }).expect("codegen failed");

    gen_mod_rs_in_dir(dir);
}

pub fn list_tests_in_dir(dir: &str) -> Vec<String> {
    let mut r = Vec::new();
    for entry in fs::read_dir(dir).expect("read_dir") {
        let entry = entry.expect("entry");
        let entry_path = entry.path();
        let file_name = entry_path.as_path().file_name().unwrap().to_str().unwrap();

        // temporart files
        if file_name.ends_with(".") {
            continue;
        }

        if !file_name.ends_with(".rs") || file_name.ends_with("_pb.rs") {
            continue;
        }

        if file_name == "mod.rs" {
            continue;
        }

        r.push(file_name[..file_name.len() - ".rs".len()].to_owned());
    }

    // Make test stable
    r.sort();

    r
}

pub fn copy_tests_v2_v3(v2_dir: &str, v3_dir: &str) {
    for test_name in list_tests_in_dir(v2_dir) {
        let mut p2f = fs::File::open(&format!("{}/{}_pb.proto", v2_dir, test_name))
            .expect("open v2 .proto");
        let mut proto = String::new();
        p2f.read_to_string(&mut proto).expect("read .proto");
        drop(p2f);

        let mut r2f = fs::File::open(&format!("{}/{}.rs", v2_dir, test_name))
            .expect("open v2 .rs");
        let mut rs = String::new();
        r2f.read_to_string(&mut rs).expect("read .rs");
        drop(r2f);

        let mut p3f = fs::File::create(&format!("{}/{}_pb.proto", v3_dir, test_name))
            .expect("create v3 .proto");
        let mut r3f = fs::File::create(&format!("{}/{}.rs", v3_dir, test_name))
            .expect("create v3 .rs");

        // convert proto2 to proto3
        let proto = proto.replace("optional ", "");
        let proto = proto.replace("required ", "");
        let proto = proto.replace("syntax = \"proto2\";", "syntax = \"proto3\";");
        write!(p3f, "// generated\n").expect("write");
        write!(p3f, "{}", proto).expect("write");
        p3f.flush().expect("flush");

        write!(r3f, "// generated\n").expect("write");
        write!(r3f, "{}", rs).expect("write");
        r3f.flush().expect("flush");
    }
}
<commit_msg>Diagnostics in test<commit_after>//! Common code of `build.rs` of two tests

pub use protobuf_codegen::Customize;

use std::io::Write;
use std::io::Read;
use std::io::BufRead;
use std::io::BufReader;
use std::fs;
use std::fmt;
use std::path::Path;

use glob;

pub fn glob_simple(pattern: &str) -> Vec<String> {
    let mut r: Vec<_> = glob::glob(pattern)
        .expect("glob")
        .map(|g| {
            g.expect("item")
                .as_path()
                .to_str()
                .expect("utf-8")
                .to_owned()
        })
        .collect();
    // Make test stable
    r.sort();
    r
}

fn read_gitignore(dir: &Path) -> Vec<String> {
    let mut patterns = Vec::new();

    let gitignore = format!("{}/.gitignore", dir.display());
    let gitignore = &Path::new(&gitignore);
    if gitignore.exists() {
        let gitignore = fs::File::open(gitignore)
            .expect(&format!("open gitignore {:?}", gitignore));
        for line in BufReader::new(gitignore).lines() {
            let line = line.expect("read_line");
            if line.is_empty() || line.starts_with("#") {
                continue;
            }
            patterns.push(line);
        }
    }

    patterns
}

fn clean_recursively(dir: &Path, patterns: &[&str]) {
    assert!(dir.is_dir());

    eprintln!("cleaning ignored files in {:?}", dir);

    let gitignore_patterns = read_gitignore(dir);

    let mut patterns = patterns.to_vec();
    patterns.extend(gitignore_patterns.iter().map(String::as_str));

    let patterns_compiled: Vec<_> = patterns.iter()
        .map(|&p| glob::Pattern::new(p).expect("failed to compile pattern"))
        .collect();

    for entry in fs::read_dir(dir).expect("read_dir") {
        let entry = entry.expect("entry");
        let entry_path = entry.path();
        let file_name = entry_path.as_path().file_name().unwrap().to_str().unwrap();
        if entry.metadata().expect(&format!("metadata of {:?}", entry_path)).is_dir() {
            clean_recursively(&entry_path, &patterns);
        } else if file_name == ".gitignore" {
            // keep it
        } else {
            for pattern in &patterns_compiled {
                if pattern.matches(file_name) {
                    fs::remove_file(&entry_path).expect(&format!("remove_file {:?}", entry_path));
                    break;
                }
            }
        }
    }
}

pub fn clean_old_files() {
    clean_recursively(&Path::new("src"), &["*_pb.rs", "*_pb_proto3.rs"]);
}

#[derive(Default)]
pub struct GenInDirArgs<'a> {
    pub out_dir: &'a str,
    pub input: &'a [&'a str],
    pub includes: &'a [&'a str],
    pub customize: Customize,
}

/// Generate mod.rs from all files in a directory
pub fn gen_mod_rs_in_dir(dir: &str) {
    assert!(Path::new(dir).is_dir());

    let mut mod_rs = fs::File::create(&format!("{}/mod.rs", dir)).expect("create");

    writeln!(mod_rs, "// generated by {}", module_path!()).expect("write");
    writeln!(mod_rs, "").expect("write");

    let rs_files = glob_simple(&format!("{}/*.rs", dir));

    for rs in rs_files {
        let file_name = Path::new(&rs).file_name().expect("file_name").to_str().expect("file_name");
        if file_name == "mod.rs" {
            continue;
        }
        assert!(file_name.ends_with(".rs"));
        let mod_name = &file_name[..file_name.len() - ".rs".len()];

        if mod_name.contains("carllerche") {
            writeln!(mod_rs, r#"#[cfg(feature = "with-bytes")]"#).expect("write carllerche");
        }
        writeln!(mod_rs, "mod {};", mod_name).expect("write");
    }

    mod_rs.flush().expect("flush");
}

enum TestProtobufVersions {
    V2,
    V3,
    Google,
}

#[derive(Eq, PartialEq, Debug)]
enum ProtobufSyntax {
    V2,
    V3,
}

fn test_version_from_file_path(mut file_path: &Path) -> TestProtobufVersions {
    loop {
        let name = file_path.file_name().expect("file_name").to_str().expect("to_str");
        if name == "v2" {
            return TestProtobufVersions::V2;
        } else if name == "v3" {
            return TestProtobufVersions::V3;
        } else if name == "google" {
            return TestProtobufVersions::Google;
        }

        file_path = file_path.parent().expect("parent");
    }
}

fn test_version_from_file_content(file_path: &Path) -> ProtobufSyntax {
    let content = fs::read_to_string(file_path)
        .expect(&format!("read_to_string {:?}", file_path));
    if content.contains("syntax = \"proto2\"") {
        return ProtobufSyntax::V2;
    } else if content.contains("syntax = \"proto3\"") {
        return ProtobufSyntax::V3;
    } else {
        panic!("cannot detect protobuf version from file content: {:?}", file_path);
    }
}

fn check_test_version(file_path: &Path) {
    let expected_version_from_file_name = match test_version_from_file_path(file_path) {
        TestProtobufVersions::V2 => ProtobufSyntax::V2,
        TestProtobufVersions::V3 => ProtobufSyntax::V3,
        TestProtobufVersions::Google => return,
    };

    let version = test_version_from_file_content(file_path);
    assert_eq!(expected_version_from_file_name, version, "for file: {:?}", file_path);
}

pub fn gen_in_dir_impl<F, E>(dir: &str, include_dir: &str, gen: F)
    where
        F : for<'a> Fn(GenInDirArgs<'a>) -> Result<(), E>,
        E : fmt::Debug,
{
    info!("generating protos in {}", dir);

    let mut protos = Vec::new();
    for suffix in &[".proto", ".proto3"] {
        protos.extend(glob_simple(&format!("{}/*{}", dir, suffix)));
    }

    for file_path in &protos {
        check_test_version(Path::new(file_path));
    }

    assert!(!protos.is_empty(), "no protos found in {}", dir);

    let includes = ["../proto", include_dir];

    eprintln!("invoking protobubf compiler: out_dir: {:?}, input: {:?}, includes: {:?}",
        dir, protos, includes);

    gen(GenInDirArgs {
        out_dir: dir,
        input: &protos.iter().map(|a| a.as_ref()).collect::<Vec<&str>>(),
        includes: &includes,
        .. Default::default()
    }).expect("codegen failed");

    gen_mod_rs_in_dir(dir);
}

pub fn list_tests_in_dir(dir: &str) -> Vec<String> {
    let mut r = Vec::new();
    for entry in fs::read_dir(dir).expect("read_dir") {
        let entry = entry.expect("entry");
        let entry_path = entry.path();
        let file_name = entry_path.as_path().file_name().unwrap().to_str().unwrap();

        // temporart files
        if file_name.ends_with(".") {
            continue;
        }

        if !file_name.ends_with(".rs") || file_name.ends_with("_pb.rs") {
            continue;
        }

        if file_name == "mod.rs" {
            continue;
        }

        r.push(file_name[..file_name.len() - ".rs".len()].to_owned());
    }

    // Make test stable
    r.sort();

    r
}

pub fn copy_tests_v2_v3(v2_dir: &str, v3_dir: &str) {
    for test_name in list_tests_in_dir(v2_dir) {
        debug!("Copying tests v2 -> v3 from test: {}", test_name);

        let mut p2f = fs::File::open(&format!("{}/{}_pb.proto", v2_dir, test_name))
            .expect("open v2 .proto");
        let mut proto = String::new();
        p2f.read_to_string(&mut proto).expect("read .proto");
        drop(p2f);

        let mut r2f = fs::File::open(&format!("{}/{}.rs", v2_dir, test_name))
            .expect("open v2 .rs");
        let mut rs = String::new();
        r2f.read_to_string(&mut rs).expect("read .rs");
        drop(r2f);

        let mut p3f = fs::File::create(&format!("{}/{}_pb.proto", v3_dir, test_name))
            .expect("create v3 .proto");
        let mut r3f = fs::File::create(&format!("{}/{}.rs", v3_dir, test_name))
            .expect("create v3 .rs");

        // convert proto2 to proto3
        let proto = proto.replace("optional ", "");
        let proto = proto.replace("required ", "");
        let proto = proto.replace("syntax = \"proto2\";", "syntax = \"proto3\";");
        write!(p3f, "// generated\n").expect("write");
        write!(p3f, "{}", proto).expect("write");
        p3f.flush().expect("flush");

        write!(r3f, "// generated\n").expect("write");
        write!(r3f, "{}", rs).expect("write");
        r3f.flush().expect("flush");
    }
}
<|endoftext|>


<commit_before>use parking_lot::RwLock;
use {yellow::{GreenNode, RedPtr}, TextUnit};

#[derive(Debug)]
pub(crate) struct RedNode {
    green: GreenNode,
    parent: Option<ParentData>,
    children: RwLock<Box<[RedChild]>>,
}

#[derive(Debug)]
enum RedChild {
    Zigot(TextUnit),
    Child(RedNode)
}

impl RedChild {
    fn set(&mut self, node: RedNode) -> &RedNode {
        match self {
            RedChild::Child(node) => return node,
            RedChild::Zigot(_) => {
                *self = RedChild::Child(node);
                match self {
                    RedChild::Child(node) => return node,
                    RedChild::Zigot(_) => unreachable!()
                }
            }
        }
    }
}

#[derive(Debug)]
struct ParentData {
    parent: RedPtr,
    start_offset: TextUnit,
    index_in_parent: usize,
}

impl RedNode {
    pub fn new_root(green: GreenNode) -> RedNode {
        RedNode::new(green, None)
    }

    fn new_child(
        green: GreenNode,
        parent: RedPtr,
        start_offset: TextUnit,
        index_in_parent: usize,
    ) -> RedNode {
        let parent_data = ParentData {
            parent,
            start_offset,
            index_in_parent,
        };
        RedNode::new(green, Some(parent_data))
    }

    fn new(green: GreenNode, parent: Option<ParentData>) -> RedNode {
        let start_offset = parent.as_ref().map(|it| it.start_offset).unwrap_or(0.into());
        let children = green.children()
            .iter()
            .scan(start_offset, |start_offset, child| {
                let res = RedChild::Zigot(*start_offset);
                *start_offset += child.text_len();
                Some(res)
            })
            .collect::<Vec<_>>()
            .into_boxed_slice();
        RedNode {
            green,
            parent,
            children: RwLock::new(children),
        }
    }

    pub(crate) fn green(&self) -> &GreenNode {
        &self.green
    }

    pub(crate) fn start_offset(&self) -> TextUnit {
        match &self.parent {
            None => 0.into(),
            Some(p) => p.start_offset,
        }
    }

    pub(crate) fn n_children(&self) -> usize {
        self.green.children().len()
    }

    pub(crate) fn get_child(&self, idx: usize) -> Option<RedPtr> {
        if idx >= self.n_children() {
            return None;
        }
        let start_offset = match &self.children.read()[idx] {
            RedChild::Child(child) => return Some(RedPtr::new(child)),
            RedChild::Zigot(start_offset) => *start_offset,
        };
        let green_children = self.green.children();
        let child =
            RedNode::new_child(green_children[idx].clone(), RedPtr::new(self), start_offset, idx);
        let mut children = self.children.write();
        let child = children[idx].set(child);
        Some(RedPtr::new(child))
    }

    pub(crate) fn parent(&self) -> Option<RedPtr> {
        Some(self.parent.as_ref()?.parent)
    }
    pub(crate) fn index_in_parent(&self) -> Option<usize> {
        Some(self.parent.as_ref()?.index_in_parent)
    }
}
<commit_msg>simplify<commit_after>use parking_lot::RwLock;
use {yellow::{GreenNode, RedPtr}, TextUnit};

#[derive(Debug)]
pub(crate) struct RedNode {
    green: GreenNode,
    parent: Option<ParentData>,
    children: RwLock<Box<[RedChild]>>,
}

#[derive(Debug)]
enum RedChild {
    Zigot(TextUnit),
    Child(RedNode)
}

impl RedChild {
    fn set(&mut self, node: RedNode) -> &RedNode {
        match self {
            RedChild::Child(node) => return node,
            RedChild::Zigot(_) => {
                *self = RedChild::Child(node);
                match self {
                    RedChild::Child(node) => return node,
                    RedChild::Zigot(_) => unreachable!()
                }
            }
        }
    }
}

#[derive(Debug)]
struct ParentData {
    parent: RedPtr,
    start_offset: TextUnit,
    index_in_parent: usize,
}

impl RedNode {
    pub fn new_root(green: GreenNode) -> RedNode {
        RedNode::new(green, None)
    }

    fn new_child(
        green: GreenNode,
        parent: RedPtr,
        start_offset: TextUnit,
        index_in_parent: usize,
    ) -> RedNode {
        let parent_data = ParentData {
            parent,
            start_offset,
            index_in_parent,
        };
        RedNode::new(green, Some(parent_data))
    }

    fn new(green: GreenNode, parent: Option<ParentData>) -> RedNode {
        let mut start_offset = parent.as_ref().map(|it| it.start_offset).unwrap_or(0.into());
        let children = green.children()
            .iter()
            .map(|child| {
                let off = start_offset;
                start_offset += child.text_len();
                RedChild::Zigot(off)
            })
            .collect::<Vec<_>>()
            .into_boxed_slice();
        RedNode {
            green,
            parent,
            children: RwLock::new(children),
        }
    }

    pub(crate) fn green(&self) -> &GreenNode {
        &self.green
    }

    pub(crate) fn start_offset(&self) -> TextUnit {
        match &self.parent {
            None => 0.into(),
            Some(p) => p.start_offset,
        }
    }

    pub(crate) fn n_children(&self) -> usize {
        self.green.children().len()
    }

    pub(crate) fn get_child(&self, idx: usize) -> Option<RedPtr> {
        if idx >= self.n_children() {
            return None;
        }
        let start_offset = match &self.children.read()[idx] {
            RedChild::Child(child) => return Some(RedPtr::new(child)),
            RedChild::Zigot(start_offset) => *start_offset,
        };
        let green_children = self.green.children();
        let child =
            RedNode::new_child(green_children[idx].clone(), RedPtr::new(self), start_offset, idx);
        let mut children = self.children.write();
        let child = children[idx].set(child);
        Some(RedPtr::new(child))
    }

    pub(crate) fn parent(&self) -> Option<RedPtr> {
        Some(self.parent.as_ref()?.parent)
    }
    pub(crate) fn index_in_parent(&self) -> Option<usize> {
        Some(self.parent.as_ref()?.index_in_parent)
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 The xi-editor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Compute line wrapping breaks for text.

use xi_rope::breaks::{BreakBuilder, Breaks, BreaksBaseMetric};
use xi_rope::spans::Spans;
use xi_rope::{Cursor, Interval, LinesMetric, Rope, RopeInfo};
use xi_trace::trace_block;
use xi_unicode::LineBreakLeafIter;

use styles::{Style, N_RESERVED_STYLES};
use width_cache::{Token, WidthCache, WidthMeasure};

struct LineBreakCursor<'a> {
    inner: Cursor<'a, RopeInfo>,
    lb_iter: LineBreakLeafIter,
    last_byte: u8,
}

impl<'a> LineBreakCursor<'a> {
    fn new(text: &'a Rope, pos: usize) -> LineBreakCursor<'a> {
        let inner = Cursor::new(text, pos);
        let lb_iter = match inner.get_leaf() {
            Some((s, offset)) => LineBreakLeafIter::new(s.as_str(), offset),
            _ => LineBreakLeafIter::default(),
        };
        LineBreakCursor { inner, lb_iter, last_byte: 0 }
    }

    // position and whether break is hard; up to caller to stop calling after EOT
    fn next(&mut self) -> (usize, bool) {
        let mut leaf = self.inner.get_leaf();
        loop {
            match leaf {
                Some((s, offset)) => {
                    let (next, hard) = self.lb_iter.next(s.as_str());
                    if next < s.len() {
                        return (self.inner.pos() - offset + next, hard);
                    }
                    if !s.is_empty() {
                        self.last_byte = s.as_bytes()[s.len() - 1];
                    }
                    leaf = self.inner.next_leaf();
                }
                // A little hacky but only reports last break as hard if final newline
                None => return (self.inner.pos(), self.last_byte == b'\n'),
            }
        }
    }
}

/// A potential opportunity to insert a break. In this representation, the widths
/// have been requested (in a batch request) but are not necessarily known until
/// the request is issued.
struct PotentialBreak {
    /// The offset within the text of the end of the word.
    pos: usize,
    /// A token referencing the width of the word, to be resolved in the width cache.
    tok: Token,
    /// Whether the break is a hard break or a soft break.
    hard: bool,
}

/// State for a rewrap in progress
struct RewrapCtx<'a, T: 'a> {
    text: &'a Rope,
    lb_cursor: LineBreakCursor<'a>,
    lb_cursor_pos: usize,
    width_cache: &'a mut WidthCache,
    client: &'a T,
    pot_breaks: Vec<PotentialBreak>,
    /// Index within `pot_breaks`
    pot_break_ix: usize,
    /// Offset of maximum break (ie hard break following edit)
    max_offset: usize,
    max_width: f64,
}

// This constant should be tuned so that the RPC takes about 1ms. Less than that,
// RPC overhead becomes significant. More than that, interactivity suffers.
const MAX_POT_BREAKS: usize = 10_000;

impl<'a, T: WidthMeasure> RewrapCtx<'a, T> {
    fn new(
        text: &'a Rope,
        /* _style_spans: &Spans<Style>, */ client: &'a T,
        max_width: f64,
        width_cache: &'a mut WidthCache,
        start: usize,
        end: usize,
    ) -> RewrapCtx<'a, T> {
        let lb_cursor_pos = start;
        let lb_cursor = LineBreakCursor::new(text, start);
        RewrapCtx {
            text,
            lb_cursor,
            lb_cursor_pos,
            width_cache,
            client,
            pot_breaks: Vec::new(),
            pot_break_ix: 0,
            max_offset: end,
            max_width,
        }
    }

    fn refill_pot_breaks(&mut self) {
        let mut req = self.width_cache.batch_req();

        self.pot_breaks.clear();
        self.pot_break_ix = 0;
        let mut pos = self.lb_cursor_pos;
        while pos < self.max_offset && self.pot_breaks.len() < MAX_POT_BREAKS {
            let (next, hard) = self.lb_cursor.next();
            // TODO: avoid allocating string
            let word = self.text.slice_to_cow(pos..next);
            let tok = req.request(N_RESERVED_STYLES, &word);
            pos = next;
            self.pot_breaks.push(PotentialBreak { pos, tok, hard });
        }
        req.resolve_pending(self.client).unwrap();
        self.lb_cursor_pos = pos;
    }

    /// Compute the next break, assuming `start` is a valid break.
    ///
    /// Invariant: `start` corresponds to the start of the word referenced by `pot_break_ix`.
    fn wrap_one_line(&mut self, start: usize) -> Option<usize> {
        let mut line_width = 0.0;
        let mut pos = start;
        while pos < self.max_offset {
            if self.pot_break_ix >= self.pot_breaks.len() {
                self.refill_pot_breaks();
            }
            let pot_break = &self.pot_breaks[self.pot_break_ix];
            if pot_break.hard {
                self.pot_break_ix += 1;
                return Some(pot_break.pos);
            }
            let width = self.width_cache.resolve(pot_break.tok);
            if line_width == 0.0 && width >= self.max_width {
                self.pot_break_ix += 1;
                return Some(pot_break.pos);
            }
            line_width += width;
            if line_width > self.max_width {
                return Some(pos);
            }
            self.pot_break_ix += 1;
            pos = pot_break.pos;
        }
        None
    }
}

/// Wrap the text (in batch mode) using width measurement.
pub fn rewrap_all<T: WidthMeasure>(
    text: &Rope,
    width_cache: &mut WidthCache,
    _style_spans: &Spans<Style>,
    client: &T,
    max_width: f64,
) -> Breaks {
    let mut ctx =
        RewrapCtx::new(text, /* style_spans, */ client, max_width, width_cache, 0, text.len());
    let mut builder = BreakBuilder::new();
    let mut pos = 0;
    while let Some(next) = ctx.wrap_one_line(pos) {
        builder.add_break(next - pos);
        pos = next;
    }
    builder.add_no_break(text.len() - pos);
    builder.build()
}

//NOTE: incremental version of rewrap_all
/// Compute a new chunk of breaks after an edit. Returns new breaks to replace
/// the old ones. The interval [start..end] represents a frontier.
fn compute_rewrap<T: WidthMeasure>(
    text: &Rope,
    width_cache: &mut WidthCache,
    /* style_spans: &Spans<Style>, */ client: &T,
    max_width: f64,
    breaks: &Breaks,
    start: usize,
    end: usize,
) -> Breaks {
    let mut line_cursor = Cursor::new(&text, end);
    let measure_end = line_cursor.next::<LinesMetric>().unwrap_or(text.len());
    let mut ctx = RewrapCtx::new(
        text,
        /* style_spans, */ client,
        max_width,
        width_cache,
        start,
        measure_end,
    );
    let mut builder = BreakBuilder::new();
    let mut pos = start;
    let mut break_cursor = Cursor::new(&breaks, end);
    let mut next_break = break_cursor.at_or_next::<BreaksBaseMetric>();
    loop {
        // iterate newly computed breaks and existing breaks until they converge
        if let Some(new_next) = ctx.wrap_one_line(pos) {
            while let Some(old_next) = next_break {
                if old_next >= new_next {
                    break;
                }
                next_break = break_cursor.next::<BreaksBaseMetric>();
            }
            // TODO: we might be able to tighten the logic, avoiding this last break,
            // in some cases (resulting in a smaller delta).
            builder.add_break(new_next - pos);
            if let Some(old_next) = next_break {
                if new_next == old_next {
                    // Breaking process has converged.
                    break;
                }
            }
            pos = new_next;
        } else {
            // EOF
            builder.add_no_break(text.len() - pos);
            break;
        }
    }
    builder.build()
}

pub fn rewrap<T: WidthMeasure>(
    breaks: &mut Breaks,
    text: &Rope,
    width_cache: &mut WidthCache, // _style_spans: &Spans<Style>,
    client: &T,
    iv: Interval,
    newsize: usize,
    max_width: f64,
) {
    let _t = trace_block("linewrap::rewrap_width", &["core"]);
    // First, remove any breaks in edited section.
    let mut builder = BreakBuilder::new();
    builder.add_no_break(newsize);
    let edit_iv = Interval::new(iv.start(), iv.end());
    breaks.edit(edit_iv, builder.build());
    // At this point, breaks is aligned with text.

    let mut start = iv.start();
    let end = start + newsize;
    // [start..end] is edited range in text

    // Find a point earlier than any possible breaks change. For simplicity, this is the
    // beginning of the paragraph, but going back two breaks would be better.
    let mut cursor = Cursor::new(&text, start);
    start = cursor.at_or_prev::<LinesMetric>().unwrap_or(0);

    let new_breaks = compute_rewrap(
        text,
        width_cache, /* style_spans, */
        client,
        max_width,
        breaks,
        start,
        end,
    );
    let edit_iv = Interval::new(start, start + new_breaks.len());
    breaks.edit(edit_iv, new_breaks);
}

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

    #[test]
    fn column_breaks_basic() {
        let text: Rope = "every wordthing should getits own".into();
        let mut width_cache = WidthCache::new();
        let spans: Spans<Style> = Spans::default();
        let breaks = rewrap_all(&text, &mut width_cache, &spans, &CodepointMono, 4.0);
        let breaks_vec = {
            let mut cursor = Cursor::new(&breaks, 0);
            cursor.get_leaf().unwrap().0.get_data_cloned()
        };
        assert_eq!(breaks_vec, vec![6, 16, 23, 30]);
    }

    #[test]
    fn column_breaks_trailing_newline() {
        let text: Rope = "every wordthing should getits ow\n".into();
        let mut width_cache = WidthCache::new();
        let spans: Spans<Style> = Spans::default();
        let breaks = rewrap_all(&text, &mut width_cache, &spans, &CodepointMono, 4.0);
        let breaks_vec = {
            let mut cursor = Cursor::new(&breaks, 0);
            cursor.get_leaf().unwrap().0.get_data_cloned()
        };
        assert_eq!(breaks_vec, vec![6, 16, 23, 30, 33]);
    }

    #[test]
    fn column_breaks_hard_soft() {
        let text: Rope = "so\nevery wordthing should getits own".into();
        let mut width_cache = WidthCache::new();
        let spans: Spans<Style> = Spans::default();
        let breaks = rewrap_all(&text, &mut width_cache, &spans, &CodepointMono, 4.0);
        let breaks_vec = {
            let mut cursor = Cursor::new(&breaks, 0);
            cursor.get_leaf().unwrap().0.get_data_cloned()
        };
        assert_eq!(breaks_vec, vec![3, 9, 19, 26, 33]);
    }
}
<commit_msg>Always add soft breaks before hard<commit_after>// Copyright 2016 The xi-editor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Compute line wrapping breaks for text.

use xi_rope::breaks::{BreakBuilder, Breaks, BreaksBaseMetric};
use xi_rope::spans::Spans;
use xi_rope::{Cursor, Interval, LinesMetric, Rope, RopeInfo};
use xi_trace::trace_block;
use xi_unicode::LineBreakLeafIter;

use styles::{Style, N_RESERVED_STYLES};
use width_cache::{Token, WidthCache, WidthMeasure};

struct LineBreakCursor<'a> {
    inner: Cursor<'a, RopeInfo>,
    lb_iter: LineBreakLeafIter,
    last_byte: u8,
}

impl<'a> LineBreakCursor<'a> {
    fn new(text: &'a Rope, pos: usize) -> LineBreakCursor<'a> {
        let inner = Cursor::new(text, pos);
        let lb_iter = match inner.get_leaf() {
            Some((s, offset)) => LineBreakLeafIter::new(s.as_str(), offset),
            _ => LineBreakLeafIter::default(),
        };
        LineBreakCursor { inner, lb_iter, last_byte: 0 }
    }

    // position and whether break is hard; up to caller to stop calling after EOT
    fn next(&mut self) -> (usize, bool) {
        let mut leaf = self.inner.get_leaf();
        loop {
            match leaf {
                Some((s, offset)) => {
                    let (next, hard) = self.lb_iter.next(s.as_str());
                    if next < s.len() {
                        return (self.inner.pos() - offset + next, hard);
                    }
                    if !s.is_empty() {
                        self.last_byte = s.as_bytes()[s.len() - 1];
                    }
                    leaf = self.inner.next_leaf();
                }
                // A little hacky but only reports last break as hard if final newline
                None => return (self.inner.pos(), self.last_byte == b'\n'),
            }
        }
    }
}

/// A potential opportunity to insert a break. In this representation, the widths
/// have been requested (in a batch request) but are not necessarily known until
/// the request is issued.
struct PotentialBreak {
    /// The offset within the text of the end of the word.
    pos: usize,
    /// A token referencing the width of the word, to be resolved in the width cache.
    tok: Token,
    /// Whether the break is a hard break or a soft break.
    hard: bool,
}

/// State for a rewrap in progress
struct RewrapCtx<'a, T: 'a> {
    text: &'a Rope,
    lb_cursor: LineBreakCursor<'a>,
    lb_cursor_pos: usize,
    width_cache: &'a mut WidthCache,
    client: &'a T,
    pot_breaks: Vec<PotentialBreak>,
    /// Index within `pot_breaks`
    pot_break_ix: usize,
    /// Offset of maximum break (ie hard break following edit)
    max_offset: usize,
    max_width: f64,
}

// This constant should be tuned so that the RPC takes about 1ms. Less than that,
// RPC overhead becomes significant. More than that, interactivity suffers.
const MAX_POT_BREAKS: usize = 10_000;

impl<'a, T: WidthMeasure> RewrapCtx<'a, T> {
    fn new(
        text: &'a Rope,
        /* _style_spans: &Spans<Style>, */ client: &'a T,
        max_width: f64,
        width_cache: &'a mut WidthCache,
        start: usize,
        end: usize,
    ) -> RewrapCtx<'a, T> {
        let lb_cursor_pos = start;
        let lb_cursor = LineBreakCursor::new(text, start);
        RewrapCtx {
            text,
            lb_cursor,
            lb_cursor_pos,
            width_cache,
            client,
            pot_breaks: Vec::new(),
            pot_break_ix: 0,
            max_offset: end,
            max_width,
        }
    }

    fn refill_pot_breaks(&mut self) {
        let mut req = self.width_cache.batch_req();

        self.pot_breaks.clear();
        self.pot_break_ix = 0;
        let mut pos = self.lb_cursor_pos;
        while pos < self.max_offset && self.pot_breaks.len() < MAX_POT_BREAKS {
            let (next, hard) = self.lb_cursor.next();
            // TODO: avoid allocating string
            let word = self.text.slice_to_cow(pos..next);
            let tok = req.request(N_RESERVED_STYLES, &word);
            pos = next;
            self.pot_breaks.push(PotentialBreak { pos, tok, hard });
        }
        req.resolve_pending(self.client).unwrap();
        self.lb_cursor_pos = pos;
    }

    /// Compute the next break, assuming `start` is a valid break.
    ///
    /// Invariant: `start` corresponds to the start of the word referenced by `pot_break_ix`.
    fn wrap_one_line(&mut self, start: usize) -> Option<usize> {
        let mut line_width = 0.0;
        let mut pos = start;
        while pos < self.max_offset {
            if self.pot_break_ix >= self.pot_breaks.len() {
                self.refill_pot_breaks();
            }
            let pot_break = &self.pot_breaks[self.pot_break_ix];
            let width = self.width_cache.resolve(pot_break.tok);
            if !pot_break.hard {
                if line_width == 0.0 && width >= self.max_width {
                    self.pot_break_ix += 1;
                    return Some(pot_break.pos);
                }
                line_width += width;
                if line_width > self.max_width {
                    return Some(pos);
                }
                self.pot_break_ix += 1;
                pos = pot_break.pos;
            } else if line_width != 0. && width + line_width > self.max_width {
                // if this is a hard break but we would have broken at the previous
                // pos otherwise, we still break at the previous pos.
                return Some(pos);
            } else {
                self.pot_break_ix += 1;
                return Some(pot_break.pos);
            }
        }
        None
    }
}

/// Wrap the text (in batch mode) using width measurement.
pub fn rewrap_all<T: WidthMeasure>(
    text: &Rope,
    width_cache: &mut WidthCache,
    _style_spans: &Spans<Style>,
    client: &T,
    max_width: f64,
) -> Breaks {
    let mut ctx =
        RewrapCtx::new(text, /* style_spans, */ client, max_width, width_cache, 0, text.len());
    let mut builder = BreakBuilder::new();
    let mut pos = 0;
    while let Some(next) = ctx.wrap_one_line(pos) {
        builder.add_break(next - pos);
        pos = next;
    }
    builder.add_no_break(text.len() - pos);
    builder.build()
}

//NOTE: incremental version of rewrap_all
/// Compute a new chunk of breaks after an edit. Returns new breaks to replace
/// the old ones. The interval [start..end] represents a frontier.
fn compute_rewrap<T: WidthMeasure>(
    text: &Rope,
    width_cache: &mut WidthCache,
    /* style_spans: &Spans<Style>, */ client: &T,
    max_width: f64,
    breaks: &Breaks,
    start: usize,
    end: usize,
) -> Breaks {
    let mut line_cursor = Cursor::new(&text, end);
    let measure_end = line_cursor.next::<LinesMetric>().unwrap_or(text.len());
    let mut ctx = RewrapCtx::new(
        text,
        /* style_spans, */ client,
        max_width,
        width_cache,
        start,
        measure_end,
    );
    let mut builder = BreakBuilder::new();
    let mut pos = start;
    let mut break_cursor = Cursor::new(&breaks, end);
    let mut next_break = break_cursor.at_or_next::<BreaksBaseMetric>();
    loop {
        // iterate newly computed breaks and existing breaks until they converge
        if let Some(new_next) = ctx.wrap_one_line(pos) {
            while let Some(old_next) = next_break {
                if old_next >= new_next {
                    break;
                }
                next_break = break_cursor.next::<BreaksBaseMetric>();
            }
            // TODO: we might be able to tighten the logic, avoiding this last break,
            // in some cases (resulting in a smaller delta).
            builder.add_break(new_next - pos);
            if let Some(old_next) = next_break {
                if new_next == old_next {
                    // Breaking process has converged.
                    break;
                }
            }
            pos = new_next;
        } else {
            // EOF
            builder.add_no_break(text.len() - pos);
            break;
        }
    }
    builder.build()
}

pub fn rewrap<T: WidthMeasure>(
    breaks: &mut Breaks,
    text: &Rope,
    width_cache: &mut WidthCache, // _style_spans: &Spans<Style>,
    client: &T,
    iv: Interval,
    newsize: usize,
    max_width: f64,
) {
    let _t = trace_block("linewrap::rewrap_width", &["core"]);
    // First, remove any breaks in edited section.
    let mut builder = BreakBuilder::new();
    builder.add_no_break(newsize);
    let edit_iv = Interval::new(iv.start(), iv.end());
    breaks.edit(edit_iv, builder.build());
    // At this point, breaks is aligned with text.

    let mut start = iv.start();
    let end = start + newsize;
    // [start..end] is edited range in text

    // Find a point earlier than any possible breaks change. For simplicity, this is the
    // beginning of the paragraph, but going back two breaks would be better.
    let mut cursor = Cursor::new(&text, start);
    start = cursor.at_or_prev::<LinesMetric>().unwrap_or(0);

    let new_breaks = compute_rewrap(
        text,
        width_cache, /* style_spans, */
        client,
        max_width,
        breaks,
        start,
        end,
    );
    let edit_iv = Interval::new(start, start + new_breaks.len());
    breaks.edit(edit_iv, new_breaks);
}

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

    #[test]
    fn column_breaks_basic() {
        let text: Rope = "every wordthing should getits own".into();
        let mut width_cache = WidthCache::new();
        let spans: Spans<Style> = Spans::default();
        let breaks = rewrap_all(&text, &mut width_cache, &spans, &CodepointMono, 4.0);
        let breaks_vec = {
            let mut cursor = Cursor::new(&breaks, 0);
            cursor.get_leaf().unwrap().0.get_data_cloned()
        };
        assert_eq!(breaks_vec, vec![6, 16, 23, 30]);
    }

    #[test]
    fn column_breaks_trailing_newline() {
        let text: Rope = "every wordthing should getits ow\n".into();
        let mut width_cache = WidthCache::new();
        let spans: Spans<Style> = Spans::default();
        let breaks = rewrap_all(&text, &mut width_cache, &spans, &CodepointMono, 4.0);
        let breaks_vec = {
            let mut cursor = Cursor::new(&breaks, 0);
            cursor.get_leaf().unwrap().0.get_data_cloned()
        };
        assert_eq!(breaks_vec, vec![6, 16, 23, 30, 33]);
    }

    #[test]
    fn soft_before_hard() {
        let text: Rope = "create abreak between THESE TWO\nwords andbreakcorrectlyhere\nplz".into();
        let mut width_cache = WidthCache::new();
        let spans: Spans<Style> = Spans::default();
        let breaks = rewrap_all(&text, &mut width_cache, &spans, &CodepointMono, 7.0);
        let breaks_vec = {
            let mut cursor = Cursor::new(&breaks, 0);
            cursor.get_leaf().unwrap().0.get_data_cloned()
        };
        assert_eq!(breaks_vec, vec![7, 14, 22, 28, 32, 38, 60]);
    }

    #[test]
    fn column_breaks_hard_soft() {
        let text: Rope = "so\nevery wordthing should getits own".into();
        let mut width_cache = WidthCache::new();
        let spans: Spans<Style> = Spans::default();
        let breaks = rewrap_all(&text, &mut width_cache, &spans, &CodepointMono, 4.0);
        let breaks_vec = {
            let mut cursor = Cursor::new(&breaks, 0);
            cursor.get_leaf().unwrap().0.get_data_cloned()
        };
        assert_eq!(breaks_vec, vec![3, 9, 19, 26, 33]);
    }
}
<|endoftext|>


<commit_before>use core::fmt;
use core::ops::{Deref, DerefMut};

/// Pads and aligns a value to the length of a cache line.
///
/// In concurrent programming, sometimes it is desirable to make sure commonly accessed pieces of
/// data are not placed into the same cache line. Updating an atomic value invalides the whole
/// cache line it belongs to, which makes the next access to the same cache line slower for other
/// CPU cores. Use `CachePadded` to ensure updating one piece of data doesn't invalidate other
/// cached data.
///
/// # Size and alignment
///
/// Cache lines are assumed to be N bytes long, depending on the architecture:
///
/// * On x86-64 and aarch64, N = 128.
/// * On all others, N = 64.
///
/// Note that N is just a reasonable guess and is not guaranteed to match the actual cache line
/// length of the machine the program is running on. On modern Intel architectures, spatial
/// prefetcher is pulling pairs of 64-byte cache lines at a time, so we pessimistically assume that
/// cache lines are 128 bytes long.
///
/// The size of `CachePadded<T>` is the smallest multiple of N bytes large enough to accommodate
/// a value of type `T`.
///
/// The alignment of `CachePadded<T>` is the maximum of N bytes and the alignment of `T`.
///
/// # Examples
///
/// Alignment and padding:
///
/// ```
/// use crossbeam_utils::CachePadded;
///
/// let array = [CachePadded::new(1i8), CachePadded::new(2i8)];
/// let addr1 = &*array[0] as *const i8 as usize;
/// let addr2 = &*array[1] as *const i8 as usize;
///
/// assert!(addr2 - addr1 >= 64);
/// assert_eq!(addr1 % 64, 0);
/// assert_eq!(addr2 % 64, 0);
/// ```
///
/// When building a concurrent queue with a head and a tail index, it is wise to place them in
/// different cache lines so that concurrent threads pushing and popping elements don't invalidate
/// each other's cache lines:
///
/// ```
/// use crossbeam_utils::CachePadded;
/// use std::sync::atomic::AtomicUsize;
///
/// struct Queue<T> {
///     head: CachePadded<AtomicUsize>,
///     tail: CachePadded<AtomicUsize>,
///     buffer: *mut T,
/// }
/// ```
#[derive(Clone, Copy, Default, Hash, PartialEq, Eq)]
// Starting from Intel's Sandy Bridge, spatial prefetcher is now pulling pairs of 64-byte cache
// lines at a time, so we have to align to 128 bytes rather than 64.
//
// Sources:
// - https://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-optimization-manual.pdf
// - https://github.com/facebook/folly/blob/1b5288e6eea6df074758f877c849b6e73bbb9fbb/folly/lang/Align.h#L107
//
// ARM's big.LITTLE architecture has asymmetric cores and "big" cores have 128 byte cache line size
// Sources:
// - https://www.mono-project.com/news/2016/09/12/arm64-icache/
//
#[cfg_attr(any(target_arch = "x86_64", target_arch = "aarch64"), repr(align(128)))]
#[cfg_attr(
    not(any(target_arch = "x86_64", target_arch = "aarch64")),
    repr(align(64))
)]
pub struct CachePadded<T> {
    value: T,
}

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

impl<T> CachePadded<T> {
    /// Pads and aligns a value to the length of a cache line.
    ///
    /// # Examples
    ///
    /// ```
    /// use crossbeam_utils::CachePadded;
    ///
    /// let padded_value = CachePadded::new(1);
    /// ```
    #[cfg(not(feature = "nightly"))]
    pub fn new(t: T) -> CachePadded<T> {
        CachePadded::<T> { value: t }
    }

    /// Pads and aligns a value to the length of a cache line.
    ///
    /// # Examples
    ///
    /// ```
    /// use crossbeam_utils::CachePadded;
    ///
    /// let padded_value = CachePadded::new(1);
    /// ```
    #[cfg(feature = "nightly")]
    pub const fn new(t: T) -> CachePadded<T> {
        CachePadded::<T> { value: t }
    }

    /// Returns the inner value.
    ///
    /// # Examples
    ///
    /// ```
    /// use crossbeam_utils::CachePadded;
    ///
    /// let padded_value = CachePadded::new(7);
    /// let value = padded_value.into_inner();
    /// assert_eq!(value, 7);
    /// ```
    pub fn into_inner(self) -> T {
        self.value
    }
}

impl<T> Deref for CachePadded<T> {
    type Target = T;

    fn deref(&self) -> &T {
        &self.value
    }
}

impl<T> DerefMut for CachePadded<T> {
    fn deref_mut(&mut self) -> &mut T {
        &mut self.value
    }
}

impl<T: fmt::Debug> fmt::Debug for CachePadded<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("CachePadded")
            .field("value", &self.value)
            .finish()
    }
}

impl<T> From<T> for CachePadded<T> {
    fn from(t: T) -> Self {
        CachePadded::new(t)
    }
}
<commit_msg>add const_fn support for the stable compiler<commit_after>use core::fmt;
use core::ops::{Deref, DerefMut};

/// Pads and aligns a value to the length of a cache line.
///
/// In concurrent programming, sometimes it is desirable to make sure commonly accessed pieces of
/// data are not placed into the same cache line. Updating an atomic value invalides the whole
/// cache line it belongs to, which makes the next access to the same cache line slower for other
/// CPU cores. Use `CachePadded` to ensure updating one piece of data doesn't invalidate other
/// cached data.
///
/// # Size and alignment
///
/// Cache lines are assumed to be N bytes long, depending on the architecture:
///
/// * On x86-64 and aarch64, N = 128.
/// * On all others, N = 64.
///
/// Note that N is just a reasonable guess and is not guaranteed to match the actual cache line
/// length of the machine the program is running on. On modern Intel architectures, spatial
/// prefetcher is pulling pairs of 64-byte cache lines at a time, so we pessimistically assume that
/// cache lines are 128 bytes long.
///
/// The size of `CachePadded<T>` is the smallest multiple of N bytes large enough to accommodate
/// a value of type `T`.
///
/// The alignment of `CachePadded<T>` is the maximum of N bytes and the alignment of `T`.
///
/// # Examples
///
/// Alignment and padding:
///
/// ```
/// use crossbeam_utils::CachePadded;
///
/// let array = [CachePadded::new(1i8), CachePadded::new(2i8)];
/// let addr1 = &*array[0] as *const i8 as usize;
/// let addr2 = &*array[1] as *const i8 as usize;
///
/// assert!(addr2 - addr1 >= 64);
/// assert_eq!(addr1 % 64, 0);
/// assert_eq!(addr2 % 64, 0);
/// ```
///
/// When building a concurrent queue with a head and a tail index, it is wise to place them in
/// different cache lines so that concurrent threads pushing and popping elements don't invalidate
/// each other's cache lines:
///
/// ```
/// use crossbeam_utils::CachePadded;
/// use std::sync::atomic::AtomicUsize;
///
/// struct Queue<T> {
///     head: CachePadded<AtomicUsize>,
///     tail: CachePadded<AtomicUsize>,
///     buffer: *mut T,
/// }
/// ```
#[derive(Clone, Copy, Default, Hash, PartialEq, Eq)]
// Starting from Intel's Sandy Bridge, spatial prefetcher is now pulling pairs of 64-byte cache
// lines at a time, so we have to align to 128 bytes rather than 64.
//
// Sources:
// - https://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-optimization-manual.pdf
// - https://github.com/facebook/folly/blob/1b5288e6eea6df074758f877c849b6e73bbb9fbb/folly/lang/Align.h#L107
//
// ARM's big.LITTLE architecture has asymmetric cores and "big" cores have 128 byte cache line size
// Sources:
// - https://www.mono-project.com/news/2016/09/12/arm64-icache/
//
#[cfg_attr(any(target_arch = "x86_64", target_arch = "aarch64"), repr(align(128)))]
#[cfg_attr(
    not(any(target_arch = "x86_64", target_arch = "aarch64")),
    repr(align(64))
)]
pub struct CachePadded<T> {
    value: T,
}

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

impl<T> CachePadded<T> {
    /// Pads and aligns a value to the length of a cache line.
    ///
    /// # Examples
    ///
    /// ```
    /// use crossbeam_utils::CachePadded;
    ///
    /// let padded_value = CachePadded::new(1);
    /// ```
    pub const fn new(t: T) -> CachePadded<T> {
        CachePadded::<T> { value: t }
    }

    /// Returns the inner value.
    ///
    /// # Examples
    ///
    /// ```
    /// use crossbeam_utils::CachePadded;
    ///
    /// let padded_value = CachePadded::new(7);
    /// let value = padded_value.into_inner();
    /// assert_eq!(value, 7);
    /// ```
    pub fn into_inner(self) -> T {
        self.value
    }
}

impl<T> Deref for CachePadded<T> {
    type Target = T;

    fn deref(&self) -> &T {
        &self.value
    }
}

impl<T> DerefMut for CachePadded<T> {
    fn deref_mut(&mut self) -> &mut T {
        &mut self.value
    }
}

impl<T: fmt::Debug> fmt::Debug for CachePadded<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("CachePadded")
            .field("value", &self.value)
            .finish()
    }
}

impl<T> From<T> for CachePadded<T> {
    fn from(t: T) -> Self {
        CachePadded::new(t)
    }
}
<|endoftext|>