<commit_before>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use crate::{
    jellyfish_merkle_node::JellyfishMerkleNodeSchema, metrics::PRUNER_LEAST_READABLE_VERSION,
    pruner::db_pruner::DBPruner, stale_node_index::StaleNodeIndexSchema, OTHER_TIMERS_SECONDS,
};
use anyhow::Result;
use aptos_infallible::Mutex;
use aptos_jellyfish_merkle::StaleNodeIndex;
use aptos_logger::{error, warn};
use aptos_types::transaction::{AtomicVersion, Version};
use schemadb::{ReadOptions, SchemaBatch, SchemaIterator, DB};
use std::{
    iter::Peekable,
    sync::{atomic::Ordering, Arc},
    time::{Duration, Instant},
};

#[cfg(test)]
mod test;

pub const STATE_STORE_PRUNER_NAME: &str = "state store pruner";

pub struct StateStorePruner {
    db: Arc<DB>,
    index_min_nonpurged_version: AtomicVersion,
    index_purged_at: Mutex<Instant>,
    /// Keeps track of the target version that the pruner needs to achieve.
    target_version: AtomicVersion,
    min_readable_version: AtomicVersion,
}

impl DBPruner for StateStorePruner {
    fn name(&self) -> &'static str {
        STATE_STORE_PRUNER_NAME
    }

    fn prune(&self, _ledger_db_batch: &mut SchemaBatch, max_versions: u64) -> Result<Version> {
        if !self.is_pruning_pending() {
            return Ok(self.min_readable_version());
        }
        let min_readable_version = self.min_readable_version.load(Ordering::Relaxed);
        let target_version = self.target_version();
        return match prune_state_store(
            &self.db,
            min_readable_version,
            target_version,
            max_versions as usize,
        ) {
            Ok(new_min_readable_version) => {
                self.record_progress(new_min_readable_version);
                // Try to purge the log.
                if let Err(e) = self.maybe_purge_index() {
                    warn!(
                        error = ?e,
                        "Failed purging state node index, ignored.",
                    );
                }
                Ok(new_min_readable_version)
            }
            Err(e) => {
                error!(
                    error = ?e,
                    "Error pruning stale state nodes.",
                );
                Err(e)
                // On error, stop retrying vigorously by making next recv() blocking.
            }
        };
    }

    fn initialize_min_readable_version(&self) -> Result<Version> {
        let mut iter = self
            .db
            .iter::<StaleNodeIndexSchema>(ReadOptions::default())?;
        iter.seek_to_first();
        Ok(iter.next().transpose()?.map_or(0, |(index, _)| {
            index
                .stale_since_version
                .checked_sub(1)
                .expect("Nothing is stale since version 0.")
        }))
    }

    fn min_readable_version(&self) -> Version {
        self.min_readable_version.load(Ordering::Relaxed)
    }

    fn set_target_version(&self, target_version: Version) {
        self.target_version.store(target_version, Ordering::Relaxed);
    }

    fn target_version(&self) -> Version {
        self.target_version.load(Ordering::Relaxed)
    }

    fn record_progress(&self, min_readable_version: Version) {
        self.min_readable_version
            .store(min_readable_version, Ordering::Relaxed);
        PRUNER_LEAST_READABLE_VERSION
            .with_label_values(&["state_store"])
            .set(min_readable_version as i64);
    }
}

impl StateStorePruner {
    pub fn new(
        db: Arc<DB>,
        index_min_nonpurged_version: Version,
        index_purged_at: Instant,
    ) -> Self {
        let pruner = StateStorePruner {
            db,
            index_min_nonpurged_version: AtomicVersion::new(index_min_nonpurged_version),
            index_purged_at: Mutex::new(index_purged_at),
            target_version: AtomicVersion::new(0),
            min_readable_version: AtomicVersion::new(0),
        };
        pruner.initialize();
        pruner
    }

    /// Purge the stale node index so that after restart not too much already pruned stuff is dealt
    /// with again (although no harm is done deleting those then non-existent things.)
    ///
    /// We issue (range) deletes on the index only periodically instead of after every pruning batch
    /// to avoid sending too many deletions to the DB, which takes disk space and slows it down.
    fn maybe_purge_index(&self) -> Result<()> {
        const MIN_INTERVAL: Duration = Duration::from_secs(10);
        const MIN_VERSIONS: u64 = 60000;

        // A deletion is issued at most once in one minute and when the pruner has progressed by at
        // least 60000 versions (assuming the pruner deletes as slow as 1000 versions per second,
        // this imposes at most one minute of work in vain after restarting.)
        let now = Instant::now();
        if self.min_readable_version.load(Ordering::Relaxed) < self.index_min_nonpurged_version() {
            warn!("State pruner inconsistent, min_readable_version is {} and  index_min_non-purged_version is {}",
                self.min_readable_version.load(Ordering::Relaxed), self.index_min_nonpurged_version());
            return Ok(());
        }

        if now - *self.index_purged_at.lock() > MIN_INTERVAL
            && self.min_readable_version.load(Ordering::Relaxed)
                - self.index_min_nonpurged_version()
                + 1
                > MIN_VERSIONS
        {
            let new_min_non_purged_version = self.min_readable_version.load(Ordering::Relaxed) + 1;
            self.db.range_delete::<StaleNodeIndexSchema, Version>(
                &self.index_min_nonpurged_version(),
                &new_min_non_purged_version, // end is exclusive
            )?;
            self.index_min_nonpurged_version
                .store(new_min_non_purged_version, Ordering::Relaxed);
            *self.index_purged_at.lock() = now;
        }
        Ok(())
    }

    pub fn index_min_nonpurged_version(&self) -> Version {
        self.index_min_nonpurged_version.load(Ordering::Relaxed)
    }

    pub fn target_version(&self) -> Version {
        self.target_version.load(Ordering::Relaxed)
    }
}

pub fn prune_state_store(
    db: &DB,
    min_readable_version: Version,
    target_version: Version,
    max_versions: usize,
) -> Result<Version> {
    let indices = StaleNodeIndicesByVersionIterator::new(db, min_readable_version, target_version)?
        .take(max_versions) // Iterator<Item = Result<Vec<StaleNodeIndex>>>
        .collect::<Result<Vec<_>>>()? // now Vec<Vec<StaleNodeIndex>>
        .into_iter()
        .flatten()
        .collect::<Vec<_>>();

    if indices.is_empty() {
        Ok(min_readable_version)
    } else {
        let _timer = OTHER_TIMERS_SECONDS
            .with_label_values(&["pruner_commit"])
            .start_timer();
        let new_min_readable_version = indices.last().expect("Should exist.").stale_since_version;
        let mut batch = SchemaBatch::new();
        indices
            .into_iter()
            .try_for_each(|index| batch.delete::<JellyfishMerkleNodeSchema>(&index.node_key))?;
        db.write_schemas(batch)?;
        Ok(new_min_readable_version)
    }
}

struct StaleNodeIndicesByVersionIterator<'a> {
    inner: Peekable<SchemaIterator<'a, StaleNodeIndexSchema>>,
    target_min_readable_version: Version,
}

impl<'a> StaleNodeIndicesByVersionIterator<'a> {
    fn new(
        db: &'a DB,
        min_readable_version: Version,
        target_min_readable_version: Version,
    ) -> Result<Self> {
        let mut iter = db.iter::<StaleNodeIndexSchema>(ReadOptions::default())?;
        iter.seek(&min_readable_version)?;

        Ok(Self {
            inner: iter.peekable(),
            target_min_readable_version,
        })
    }

    fn next_result(&mut self) -> Result<Option<Vec<StaleNodeIndex>>> {
        match self.inner.next().transpose()? {
            None => Ok(None),
            Some((index, _)) => {
                let version = index.stale_since_version;
                if version > self.target_min_readable_version {
                    return Ok(None);
                }

                let mut indices = vec![index];
                while let Some(res) = self.inner.peek() {
                    if let Ok((index_ref, _)) = res {
                        if index_ref.stale_since_version != version {
                            break;
                        }
                    }

                    let (index, _) = self.inner.next().transpose()?.expect("Should be Some.");
                    indices.push(index);
                }

                Ok(Some(indices))
            }
        }
    }
}

impl<'a> Iterator for StaleNodeIndicesByVersionIterator<'a> {
    type Item = Result<Vec<StaleNodeIndex>>;

    fn next(&mut self) -> Option<Self::Item> {
        self.next_result().transpose()
    }
}
<commit_msg>[storage] state pruner: fix stale node index purging logic<commit_after>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use crate::{
    jellyfish_merkle_node::JellyfishMerkleNodeSchema, metrics::PRUNER_LEAST_READABLE_VERSION,
    pruner::db_pruner::DBPruner, stale_node_index::StaleNodeIndexSchema, OTHER_TIMERS_SECONDS,
};
use anyhow::Result;
use aptos_infallible::Mutex;
use aptos_jellyfish_merkle::StaleNodeIndex;
use aptos_logger::{error, warn};
use aptos_types::transaction::{AtomicVersion, Version};
use schemadb::{ReadOptions, SchemaBatch, SchemaIterator, DB};
use std::{
    iter::Peekable,
    sync::{atomic::Ordering, Arc},
    time::{Duration, Instant},
};

#[cfg(test)]
mod test;

pub const STATE_STORE_PRUNER_NAME: &str = "state store pruner";

pub struct StateStorePruner {
    db: Arc<DB>,
    max_index_purged_version: AtomicVersion,
    index_purged_at: Mutex<Instant>,
    /// Keeps track of the target version that the pruner needs to achieve.
    target_version: AtomicVersion,
    min_readable_version: AtomicVersion,
}

impl DBPruner for StateStorePruner {
    fn name(&self) -> &'static str {
        STATE_STORE_PRUNER_NAME
    }

    fn prune(&self, _ledger_db_batch: &mut SchemaBatch, max_versions: u64) -> Result<Version> {
        if !self.is_pruning_pending() {
            return Ok(self.min_readable_version());
        }
        let min_readable_version = self.min_readable_version.load(Ordering::Relaxed);
        let target_version = self.target_version();
        return match prune_state_store(
            &self.db,
            min_readable_version,
            target_version,
            max_versions as usize,
        ) {
            Ok(new_min_readable_version) => {
                self.record_progress(new_min_readable_version);
                // Try to purge the log.
                if let Err(e) = self.maybe_purge_index() {
                    warn!(
                        error = ?e,
                        "Failed purging state node index, ignored.",
                    );
                }
                Ok(new_min_readable_version)
            }
            Err(e) => {
                error!(
                    error = ?e,
                    "Error pruning stale state nodes.",
                );
                Err(e)
                // On error, stop retrying vigorously by making next recv() blocking.
            }
        };
    }

    fn initialize_min_readable_version(&self) -> Result<Version> {
        let mut iter = self
            .db
            .iter::<StaleNodeIndexSchema>(ReadOptions::default())?;
        iter.seek_to_first();
        Ok(iter.next().transpose()?.map_or(0, |(index, _)| {
            index
                .stale_since_version
                .checked_sub(1)
                .expect("Nothing is stale since version 0.")
        }))
    }

    fn min_readable_version(&self) -> Version {
        self.min_readable_version.load(Ordering::Relaxed)
    }

    fn set_target_version(&self, target_version: Version) {
        self.target_version.store(target_version, Ordering::Relaxed);
    }

    fn target_version(&self) -> Version {
        self.target_version.load(Ordering::Relaxed)
    }

    fn record_progress(&self, min_readable_version: Version) {
        self.min_readable_version
            .store(min_readable_version, Ordering::Relaxed);
        PRUNER_LEAST_READABLE_VERSION
            .with_label_values(&["state_store"])
            .set(min_readable_version as i64);
    }
}

impl StateStorePruner {
    pub fn new(db: Arc<DB>, max_index_purged_version: Version, index_purged_at: Instant) -> Self {
        let pruner = StateStorePruner {
            db,
            max_index_purged_version: AtomicVersion::new(max_index_purged_version),
            index_purged_at: Mutex::new(index_purged_at),
            target_version: AtomicVersion::new(0),
            min_readable_version: AtomicVersion::new(0),
        };
        pruner.initialize();
        pruner
    }

    /// Purge the stale node index so that after restart not too much already pruned stuff is dealt
    /// with again (although no harm is done deleting those then non-existent things.)
    ///
    /// We issue (range) deletes on the index only periodically instead of after every pruning batch
    /// to avoid sending too many deletions to the DB, which takes disk space and slows it down.
    fn maybe_purge_index(&self) -> Result<()> {
        const MIN_INTERVAL: Duration = Duration::from_secs(10);
        const MIN_VERSIONS: u64 = 60000;

        let min_readable_version = self.min_readable_version.load(Ordering::Relaxed);
        let max_index_purged_version = self.max_index_purged_version.load(Ordering::Relaxed);

        // A deletion is issued at most once in one minute and when the pruner has progressed by at
        // least 60000 versions (assuming the pruner deletes as slow as 1000 versions per second,
        // this imposes at most one minute of work in vain after restarting.)
        let now = Instant::now();
        if now - *self.index_purged_at.lock() > MIN_INTERVAL
            && min_readable_version - max_index_purged_version > MIN_VERSIONS
        {
            self.db.range_delete::<StaleNodeIndexSchema, Version>(
                &(max_index_purged_version + 1), // begin is inclusive
                &(min_readable_version + 1),     // end is exclusive
            )?;
            // The index items at min_readable_version has been consumed already because they
            // indicate nodes that became stale at that version, keeping that version readable,
            // hence is purged above.
            self.max_index_purged_version
                .store(min_readable_version, Ordering::Relaxed);
            *self.index_purged_at.lock() = now;
        }
        Ok(())
    }

    pub fn target_version(&self) -> Version {
        self.target_version.load(Ordering::Relaxed)
    }
}

pub fn prune_state_store(
    db: &DB,
    min_readable_version: Version,
    target_version: Version,
    max_versions: usize,
) -> Result<Version> {
    let indices = StaleNodeIndicesByVersionIterator::new(db, min_readable_version, target_version)?
        .take(max_versions) // Iterator<Item = Result<Vec<StaleNodeIndex>>>
        .collect::<Result<Vec<_>>>()? // now Vec<Vec<StaleNodeIndex>>
        .into_iter()
        .flatten()
        .collect::<Vec<_>>();

    if indices.is_empty() {
        Ok(min_readable_version)
    } else {
        let _timer = OTHER_TIMERS_SECONDS
            .with_label_values(&["pruner_commit"])
            .start_timer();
        let new_min_readable_version = indices.last().expect("Should exist.").stale_since_version;
        let mut batch = SchemaBatch::new();
        indices
            .into_iter()
            .try_for_each(|index| batch.delete::<JellyfishMerkleNodeSchema>(&index.node_key))?;
        db.write_schemas(batch)?;
        Ok(new_min_readable_version)
    }
}

struct StaleNodeIndicesByVersionIterator<'a> {
    inner: Peekable<SchemaIterator<'a, StaleNodeIndexSchema>>,
    target_min_readable_version: Version,
}

impl<'a> StaleNodeIndicesByVersionIterator<'a> {
    fn new(
        db: &'a DB,
        min_readable_version: Version,
        target_min_readable_version: Version,
    ) -> Result<Self> {
        let mut iter = db.iter::<StaleNodeIndexSchema>(ReadOptions::default())?;
        iter.seek(&min_readable_version)?;

        Ok(Self {
            inner: iter.peekable(),
            target_min_readable_version,
        })
    }

    fn next_result(&mut self) -> Result<Option<Vec<StaleNodeIndex>>> {
        match self.inner.next().transpose()? {
            None => Ok(None),
            Some((index, _)) => {
                let version = index.stale_since_version;
                if version > self.target_min_readable_version {
                    return Ok(None);
                }

                let mut indices = vec![index];
                while let Some(res) = self.inner.peek() {
                    if let Ok((index_ref, _)) = res {
                        if index_ref.stale_since_version != version {
                            break;
                        }
                    }

                    let (index, _) = self.inner.next().transpose()?.expect("Should be Some.");
                    indices.push(index);
                }

                Ok(Some(indices))
            }
        }
    }
}

impl<'a> Iterator for StaleNodeIndicesByVersionIterator<'a> {
    type Item = Result<Vec<StaleNodeIndex>>;

    fn next(&mut self) -> Option<Self::Item> {
        self.next_result().transpose()
    }
}
<|endoftext|>


<commit_before>use std::{fmt, mem, str};
use std::fmt::Show;
use std::string::String;
use serialize::{Encoder, Encodable};

#[deriving(Clone, PartialEq, PartialOrd, Ord, Eq, Hash)]
pub struct WadName { packed: u64 }
impl WadName {
    pub fn as_str_opt<'a>(&'a self) -> Option<&'a str> {
        str::from_utf8(self.as_bytes())
    }

    pub fn as_str<'a>(&'a self) -> &'a str {
        match self.as_str_opt() {
            Some(s) => s,
            None => fail!("Failed WadName.as_str(): {}", self)
        }
    }

    pub fn as_bytes<'a>(&'a self) -> &'a [u8, ..8] {
        unsafe { mem::transmute::<_, &'a [u8, ..8]>(&self.packed) }
    }

    pub fn as_mut_bytes<'a>(&'a mut self) -> &'a mut [u8, ..8] {
        unsafe { mem::transmute::<_, &'a mut [u8, ..8]>(&mut self.packed) }
    }

    pub fn into_canonical(mut self) -> WadName {
        self.canonicalise();
        self
    }

    pub fn canonicalise(&mut self) -> &mut WadName {
        let new_packed = match self.as_bytes().to_wad_name_opt() {
            Some(name) => name.packed,
            None => fail!("Malformed wad name: {}", self)
        };
        self.packed = new_packed;
        self
    }
}
impl Show for WadName {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        match self.as_str_opt() {
            Some(s) => write!(formatter, "{}", s),
            None => write!(formatter, "BadWadName({} {} {} {} {} {} {} {})",
                           self.as_bytes()[0], self.as_bytes()[1],
                           self.as_bytes()[2], self.as_bytes()[3],
                           self.as_bytes()[4], self.as_bytes()[5],
                           self.as_bytes()[6], self.as_bytes()[7])
        }
    }
}
impl<S: Encoder<E>, E> Encodable<S, E> for WadName {
    fn encode(&self, encoder: &mut S) -> Result<(), E> {
        match self.as_str_opt() {
            Some(s) => s.encode(encoder),
            None => fail!("Cannot encode WadName {}", self)
        }
    }
}

pub trait WadNameCast : Show {
    fn to_wad_name_opt(&self) -> Option<WadName>;
    fn to_wad_name(&self) -> WadName {
        match self.to_wad_name_opt() {
            Some(n) => n,
            None => fail!("Malformed WadName cast {}", self)
        }
    }
}
impl<'a> WadNameCast for &'a [u8] {
    fn to_wad_name_opt(&self) -> Option<WadName> {
        let mut name = [0u8, ..8];
        let mut nulled = false;
        for (dest, src) in name.iter_mut().zip(self.iter()) {
            let new_byte = match src.to_ascii_opt() {
                Some(ascii) => match ascii.to_uppercase().to_byte() {
                    b@b'A'..b'Z' | b@b'0'..b'9' | b@b'_' | b@b'-' |
                    b@b'[' | b@b']' | b@b'\\' => b,
                    b'\0' => { nulled = true; break },
                    b => {
                        debug!("Bailed on ascii {}", b);
                        return None;
                    }
                },
                None => {
                    debug!("Bailed on non-ascii {}", src);
                    return None;
                }
            };
            *dest = new_byte;
        }
        if !nulled && self.len() > 8 {
            debug!("Bailed on '{}' {} {}", str::from_utf8(*self),
                                           self.len(), !nulled);
            return None; }
        Some(WadName { packed: unsafe { mem::transmute(name) } })
    }
}
impl<'a> WadNameCast for &'a str {
    fn to_wad_name_opt(&self) -> Option<WadName> {
        self.as_bytes().to_wad_name_opt()
    }
}
impl WadNameCast for String {
    fn to_wad_name_opt(&self) -> Option<WadName> {
        self.as_bytes().to_wad_name_opt()
    }
}

mod test {
    #[test]
    fn test_wad_name() {
        assert_eq!("".to_wad_name().as_str().unwrap(),
                   "\0\0\0\0\0\0\0\0");
        assert_eq!("\0".to_wad_name().as_str().unwrap(),
                   "\0\0\0\0\0\0\0\0");
        assert_eq!("\01234567".to_wad_name().as_str().unwrap(),
                   "\0\0\0\0\0\0\0\0");
        assert_eq!("A".to_wad_name().as_str().unwrap(),
                   "A\0\0\0\0\0\0\0");
        assert_eq!("1234567".to_wad_name().as_str().unwrap(),
                   "1234567\0");
        assert_eq!("12345678".to_wad_name().as_str().unwrap(),
                   "12345678");
        assert_eq!("123\05678".to_wad_name().as_str().unwrap(),
                   "123\0\0\0\0\0");
        assert_eq!("SKY1".to_wad_name().as_str().unwrap(),
                   "SKY1\0\0\0\0");
        assert_eq!("-".to_wad_name().as_str().unwrap(),
                   "-\0\0\0\0\0\0\0");
        assert_eq!("_".to_wad_name().as_str().unwrap(),
                   "_\0\0\0\0\0\0\0");

        assert!("123456789".to_wad_name().is_none());
        assert!("1234\xfb".to_wad_name().is_none());
        assert!("\xff123".to_wad_name().is_none());
        assert!("$$ASDF_".to_wad_name().is_none());
        assert!("123456789\0".to_wad_name().is_none());
    }
}

<commit_msg>Decodable WadName. Fixed WadName Tests.<commit_after>use std::{fmt, mem, str};
use std::fmt::Show;
use std::string::String;
use serialize::{Encoder, Encodable, Decoder, Decodable};

#[deriving(Clone, PartialEq, PartialOrd, Ord, Eq, Hash)]
pub struct WadName { packed: u64 }
impl WadName {
    pub fn as_str_opt<'a>(&'a self) -> Option<&'a str> {
        str::from_utf8(self.as_bytes())
    }

    pub fn as_str<'a>(&'a self) -> &'a str {
        match self.as_str_opt() {
            Some(s) => s,
            None => fail!("Failed WadName.as_str(): {}", self)
        }
    }

    pub fn as_bytes<'a>(&'a self) -> &'a [u8, ..8] {
        unsafe { mem::transmute::<_, &'a [u8, ..8]>(&self.packed) }
    }

    pub fn as_mut_bytes<'a>(&'a mut self) -> &'a mut [u8, ..8] {
        unsafe { mem::transmute::<_, &'a mut [u8, ..8]>(&mut self.packed) }
    }

    pub fn into_canonical(mut self) -> WadName {
        self.canonicalise();
        self
    }

    pub fn canonicalise(&mut self) -> &mut WadName {
        let new_packed = match self.as_bytes().to_wad_name_opt() {
            Some(name) => name.packed,
            None => fail!("Malformed wad name: {}", self)
        };
        self.packed = new_packed;
        self
    }
}
impl Show for WadName {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        match self.as_str_opt() {
            Some(s) => write!(formatter, "{}", s),
            None => write!(formatter, "BadWadName({} {} {} {} {} {} {} {})",
                           self.as_bytes()[0], self.as_bytes()[1],
                           self.as_bytes()[2], self.as_bytes()[3],
                           self.as_bytes()[4], self.as_bytes()[5],
                           self.as_bytes()[6], self.as_bytes()[7])
        }
    }
}
impl<S: Encoder<E>, E> Encodable<S, E> for WadName {
    fn encode(&self, encoder: &mut S) -> Result<(), E> {
        match self.as_str_opt() {
            Some(s) => s.encode(encoder),
            None => fail!("Cannot encode WadName {}", self)
        }
    }
}

impl<S: Decoder<E>, E> Decodable<S, E> for WadName {
    fn decode(decoder: &mut S) -> Result<WadName, E> {
        decoder.read_str()
            .and_then(|s| {
                match s.to_wad_name_opt() {
                    Some(name) => Ok(name),
                    None => Err(decoder.error("Could not decode WadName."))
                }
            })
    }
}

pub trait WadNameCast : Show {
    fn to_wad_name_opt(&self) -> Option<WadName>;
    fn to_wad_name(&self) -> WadName {
        match self.to_wad_name_opt() {
            Some(n) => n,
            None => fail!("Malformed WadName cast {}", self)
        }
    }
}
impl<'a> WadNameCast for &'a [u8] {
    fn to_wad_name_opt(&self) -> Option<WadName> {
        let mut name = [0u8, ..8];
        let mut nulled = false;
        for (dest, src) in name.iter_mut().zip(self.iter()) {
            let new_byte = match src.to_ascii_opt() {
                Some(ascii) => match ascii.to_uppercase().to_byte() {
                    b@b'A'..b'Z' | b@b'0'..b'9' | b@b'_' | b@b'-' |
                    b@b'[' | b@b']' | b@b'\\' => b,
                    b'\0' => { nulled = true; break },
                    b => {
                        debug!("Bailed on ascii {}", b);
                        return None;
                    }
                },
                None => {
                    debug!("Bailed on non-ascii {}", src);
                    return None;
                }
            };
            *dest = new_byte;
        }
        if !nulled && self.len() > 8 {
            debug!("Bailed on '{}' {} {}", str::from_utf8(*self),
                                           self.len(), !nulled);
            return None; }
        Some(WadName { packed: unsafe { mem::transmute(name) } })
    }
}
impl<'a> WadNameCast for &'a str {
    fn to_wad_name_opt(&self) -> Option<WadName> {
        self.as_bytes().to_wad_name_opt()
    }
}
impl WadNameCast for String {
    fn to_wad_name_opt(&self) -> Option<WadName> {
        self.as_bytes().to_wad_name_opt()
    }
}

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

    #[test]
    fn test_wad_name() {
        assert_eq!("".to_wad_name().as_str(), "\0\0\0\0\0\0\0\0");
        assert_eq!("\0".to_wad_name().as_str(), "\0\0\0\0\0\0\0\0");
        assert_eq!("\01234567".to_wad_name().as_str(), "\0\0\0\0\0\0\0\0");
        assert_eq!("A".to_wad_name().as_str(), "A\0\0\0\0\0\0\0");
        assert_eq!("1234567".to_wad_name().as_str(), "1234567\0");
        assert_eq!("12345678".to_wad_name().as_str(), "12345678");
        assert_eq!("123\05678".to_wad_name().as_str(), "123\0\0\0\0\0");
        assert_eq!("SKY1".to_wad_name().as_str(), "SKY1\0\0\0\0");
        assert_eq!("-".to_wad_name().as_str(), "-\0\0\0\0\0\0\0");
        assert_eq!("_".to_wad_name().as_str(), "_\0\0\0\0\0\0\0");

        assert!("123456789".to_wad_name_opt().is_none());
        assert!("1234\xfb".to_wad_name_opt().is_none());
        assert!("\xff123".to_wad_name_opt().is_none());
        assert!("$$ASDF_".to_wad_name_opt().is_none());
        assert!("123456789\0".to_wad_name_opt().is_none());
    }
}

<|endoftext|>


<commit_before>// Implements http://rosettacode.org/wiki/Dijkstra's_algorithm

use std::collections::{HashMap, PriorityQueue, DList, Deque};
use std::collections::hashmap::{Occupied, Vacant};
use std::uint;

type Node = uint;
type Cost = uint;
type Edge = (Node, Node);

/// The DistPair struct is for the Priority Queue.
#[deriving(Eq, PartialEq, PartialOrd)]
struct DistPair(Node, Cost);
impl Ord for DistPair {
    fn cmp(&self, other:&DistPair) -> Ordering {
        let DistPair(_, dist_a) = *self;
        let DistPair(_, dist_b) = *other;
        dist_b.cmp(&dist_a) //Intentionally reversed
    }
}

/// Graph structure, represented as an Adjancency List.
struct Graph<'a> {
    vertices: Vec<&'a str>,
    adj_list: Vec<Vec<Node>>,
    costs: HashMap<Edge, Cost>,
}

impl<'a> Graph<'a> {
    fn new() -> Graph<'a> {
        let vertices:Vec<&str> = Vec::new();
        let adj_list:Vec<Vec<Node>> = Vec::new();
        let costs:HashMap<Edge, Cost> = HashMap::new();

        Graph{vertices: vertices, adj_list: adj_list, costs: costs}
    }

    /// Returns the index of the vertex, or None if vertex
    /// not found.
    fn vertex_index(&self, vertex: &str) -> Option<Node> {
        for (idx, &v) in self.vertices.iter().enumerate(){
            if v == vertex{return Some(idx)}
        }
        return None
    }

    /// Returns the index of the vertex. If vertex is not found, inserts
    /// the vertex.
    fn get_or_insert_vertex(&mut self, vertex: &'a str) -> Node {
        self.vertex_index(vertex)
            .unwrap_or_else( || {
                self.adj_list.push(Vec::new());
                self.vertices.push(vertex);
                self.vertices.len()-1
            })
    }

    /// Adds the given edge to the graph.
    fn add_edge(&mut self, from: &'a str, to: &'a str, cost: uint) {
        let from_idx = self.get_or_insert_vertex(from);
        let to_idx = self.get_or_insert_vertex(to);

        match self.costs.entry((from_idx, to_idx)) {
            Vacant(entry)   => {
                self.adj_list.get_mut(from_idx).push(to_idx);
                entry.set(cost);
            },
            Occupied(entry) => {
                *entry.into_mut() = cost;
            }
        };
    }

    /// Implements Dijkstra's Algorithm. This uses a Priority Queue to
    /// determine which vertex to visit first. Terminates on discovering
    /// the target vertex.
    ///
    /// Returns vector of vertices representing the path, or an empty vector
    /// if there's no path, or if the source or target is not in the graph.
    fn dijkstra(&self, source: &str, target: &str) -> Vec<&str> {
        let num_vert = self.vertices.len();
        let mut dist:Vec<uint> = Vec::from_elem(num_vert, uint::MAX); //Close enough to infinity
        let mut prev:HashMap<Node, Node> = HashMap::new();
        let mut queue:PriorityQueue<DistPair> = PriorityQueue::new();

        let source_idx = match self.vertex_index(source) {
            Some(idx) => idx,
            None      => return Vec::new() // Source not in graph, return empty path.
        };

        let target_idx = match self.vertex_index(target) {
            Some(idx) => idx,
            None      => return Vec::new() // Target not in graph, return empty path.
        };

        *dist.get_mut(source_idx) = 0u;
        queue.push(DistPair(source_idx, dist[source_idx]));

        loop{
            match queue.pop() {
                None     => break,
                Some(DistPair(u, dist_u)) => {
                    for &v in self.adj_list[u].iter() {
                        let cost_uv = match self.costs.find(&(u, v)) {
                            Some(&x) => x,
                            None    => uint::MAX,
                        };
                        let alt = dist_u + cost_uv;
                        if alt < dist[v] {
                            match prev.entry(v) {
                                Vacant(entry) => {entry.set(u);},
                                Occupied(entry) => {
                                    *entry.into_mut() = u;
                                }
                            }
                            *dist.get_mut(v) = alt;
                            queue.push(DistPair(v, dist[v]));
                        }
                        if v == target_idx { break; }
                    }
                }
            };
        }

        let mut temp_path:DList<&str> = DList::new();
        let mut curr = target_idx;
        temp_path.push_front(self.vertices[curr]);
        loop{
            match prev.find(&curr){
                Some(&parent) => {
                    curr = parent;
                    temp_path.push_front(self.vertices[curr]);
                    if curr == source_idx { break; }
                },
                None => return Vec::new(),
            }
        }
        return temp_path.into_iter().collect();
    }
}

#[test]
fn test_dijkstras() {
    let mut graph = Graph::new();
    graph.add_edge("a", "b", 7);
    graph.add_edge("b", "c", 10);
    graph.add_edge("c", "d", 5);
    graph.add_edge("a", "d", 30);
    graph.add_edge("y", "z", 10); //Disconnected from the rest

    assert_eq!(graph.dijkstra("a", "d"), vec!["a", "b", "c", "d"]);
    assert_eq!(graph.dijkstra("a", "y"), vec![]);
    assert_eq!(graph.dijkstra("e", "y"), vec![]);
    assert_eq!(graph.dijkstra("a", "e"), vec![]);
}

#[cfg(not(test))]
fn main(){
    let mut graph = Graph::new();
    graph.add_edge("a", "b", 7);
    graph.add_edge("a", "c", 9);
    graph.add_edge("a", "f", 14);
    graph.add_edge("b", "c", 10);
    graph.add_edge("b", "d", 15);
    graph.add_edge("c", "d", 11);
    graph.add_edge("c", "f", 2);
    graph.add_edge("d", "e", 6);
    graph.add_edge("e", "f", 9);

    let path = graph.dijkstra("a", "e");
    println!("Path is: {}", path);
}

<commit_msg>Style fix<commit_after>// Implements http://rosettacode.org/wiki/Dijkstra's_algorithm

use std::collections::{HashMap, PriorityQueue, DList, Deque};
use std::collections::hashmap::{Occupied, Vacant};
use std::uint;

type Node = uint;
type Cost = uint;
type Edge = (Node, Node);

/// The DistPair struct is for the Priority Queue.
#[deriving(Eq, PartialEq, PartialOrd)]
struct DistPair(Node, Cost);
impl Ord for DistPair {
    fn cmp(&self, other:&DistPair) -> Ordering {
        let DistPair(_, dist_a) = *self;
        let DistPair(_, dist_b) = *other;
        dist_b.cmp(&dist_a) //Intentionally reversed
    }
}

/// Graph structure, represented as an Adjancency List.
struct Graph<'a> {
    vertices: Vec<&'a str>,
    adj_list: Vec<Vec<Node>>,
    costs: HashMap<Edge, Cost>,
}

impl<'a> Graph<'a> {
    fn new() -> Graph<'a> {
        let vertices:Vec<&str> = Vec::new();
        let adj_list:Vec<Vec<Node>> = Vec::new();
        let costs:HashMap<Edge, Cost> = HashMap::new();

        Graph{vertices: vertices, adj_list: adj_list, costs: costs}
    }

    /// Returns the index of the vertex, or None if vertex
    /// not found.
    fn vertex_index(&self, vertex: &str) -> Option<Node> {
        for (idx, &v) in self.vertices.iter().enumerate(){
            if v == vertex{return Some(idx)}
        }
        return None
    }

    /// Returns the index of the vertex. If vertex is not found, inserts
    /// the vertex.
    fn get_or_insert_vertex(&mut self, vertex: &'a str) -> Node {
        self.vertex_index(vertex)
            .unwrap_or_else( || {
                self.adj_list.push(Vec::new());
                self.vertices.push(vertex);
                self.vertices.len()-1
            })
    }

    /// Adds the given edge to the graph.
    fn add_edge(&mut self, from: &'a str, to: &'a str, cost: uint) {
        let from_idx = self.get_or_insert_vertex(from);
        let to_idx = self.get_or_insert_vertex(to);

        match self.costs.entry((from_idx, to_idx)) {
            Vacant(entry)   => {
                self.adj_list.get_mut(from_idx).push(to_idx);
                entry.set(cost);
            },
            Occupied(entry) => {
                *entry.into_mut() = cost;
            }
        };
    }

    /// Implements Dijkstra's Algorithm. This uses a Priority Queue to
    /// determine which vertex to visit first. Terminates on discovering
    /// the target vertex.
    ///
    /// Returns vector of vertices representing the path, or an empty vector
    /// if there's no path, or if the source or target is not in the graph.
    fn dijkstra(&self, source: &str, target: &str) -> Vec<&str> {
        let num_vert = self.vertices.len();
        let mut dist:Vec<uint> = Vec::from_elem(num_vert, uint::MAX); //Close enough to infinity
        let mut prev:HashMap<Node, Node> = HashMap::new();
        let mut queue:PriorityQueue<DistPair> = PriorityQueue::new();

        let source_idx = match self.vertex_index(source) {
            Some(idx) => idx,
            None      => return Vec::new() // Source not in graph, return empty path.
        };

        let target_idx = match self.vertex_index(target) {
            Some(idx) => idx,
            None      => return Vec::new() // Target not in graph, return empty path.
        };

        *dist.get_mut(source_idx) = 0u;
        queue.push(DistPair(source_idx, dist[source_idx]));

        loop{
            match queue.pop() {
                None     => break,
                Some(DistPair(u, dist_u)) => {
                    for &v in self.adj_list[u].iter() {
                        let cost_uv = match self.costs.find(&(u, v)) {
                            Some(&x) => x,
                            None     => uint::MAX,
                        };
                        let alt = dist_u + cost_uv;
                        if alt < dist[v] {
                            match prev.entry(v) {
                                Vacant(entry) => {entry.set(u);},
                                Occupied(entry) => {
                                    *entry.into_mut() = u;
                                }
                            }
                            *dist.get_mut(v) = alt;
                            queue.push(DistPair(v, dist[v]));
                        }
                        if v == target_idx { break; }
                    }
                }
            };
        }

        let mut temp_path:DList<&str> = DList::new();
        let mut curr = target_idx;
        temp_path.push_front(self.vertices[curr]);
        loop{
            match prev.find(&curr){
                Some(&parent) => {
                    curr = parent;
                    temp_path.push_front(self.vertices[curr]);
                    if curr == source_idx { break; }
                },
                None => return Vec::new(),
            }
        }
        return temp_path.into_iter().collect();
    }
}

#[test]
fn test_dijkstras() {
    let mut graph = Graph::new();
    graph.add_edge("a", "b", 7);
    graph.add_edge("b", "c", 10);
    graph.add_edge("c", "d", 5);
    graph.add_edge("a", "d", 30);
    graph.add_edge("y", "z", 10); //Disconnected from the rest

    assert_eq!(graph.dijkstra("a", "d"), vec!["a", "b", "c", "d"]);
    assert_eq!(graph.dijkstra("a", "y"), vec![]);
    assert_eq!(graph.dijkstra("e", "y"), vec![]);
    assert_eq!(graph.dijkstra("a", "e"), vec![]);
}

#[cfg(not(test))]
fn main(){
    let mut graph = Graph::new();
    graph.add_edge("a", "b", 7);
    graph.add_edge("a", "c", 9);
    graph.add_edge("a", "f", 14);
    graph.add_edge("b", "c", 10);
    graph.add_edge("b", "d", 15);
    graph.add_edge("c", "d", 11);
    graph.add_edge("c", "f", 2);
    graph.add_edge("d", "e", 6);
    graph.add_edge("e", "f", 9);

    let path = graph.dijkstra("a", "e");
    println!("Path is: {}", path);
}

<|endoftext|>


<commit_before>use std::env;
use std::path::{Path, PathBuf};
use std::process::Command;

use build_helper::{output, tracked_env_var_os};

fn detect_llvm_link() -> (&'static str, &'static str) {
    // Force the link mode we want, preferring static by default, but
    // possibly overridden by `configure --enable-llvm-link-shared`.
    if tracked_env_var_os("LLVM_LINK_SHARED").is_some() {
        ("dylib", "--link-shared")
    } else {
        ("static", "--link-static")
    }
}

fn main() {
    if tracked_env_var_os("RUST_CHECK").is_some() {
        // If we're just running `check`, there's no need for LLVM to be built.
        return;
    }

    build_helper::restore_library_path();

    let target = env::var("TARGET").expect("TARGET was not set");
    let llvm_config =
        tracked_env_var_os("LLVM_CONFIG").map(|x| Some(PathBuf::from(x))).unwrap_or_else(|| {
            if let Some(dir) = tracked_env_var_os("CARGO_TARGET_DIR").map(PathBuf::from) {
                let to_test = dir
                    .parent()
                    .unwrap()
                    .parent()
                    .unwrap()
                    .join(&target)
                    .join("llvm/bin/llvm-config");
                if Command::new(&to_test).output().is_ok() {
                    return Some(to_test);
                }
            }
            None
        });

    if let Some(llvm_config) = &llvm_config {
        println!("cargo:rerun-if-changed={}", llvm_config.display());
    }
    let llvm_config = llvm_config.unwrap_or_else(|| PathBuf::from("llvm-config"));

    // Test whether we're cross-compiling LLVM. This is a pretty rare case
    // currently where we're producing an LLVM for a different platform than
    // what this build script is currently running on.
    //
    // In that case, there's no guarantee that we can actually run the target,
    // so the build system works around this by giving us the LLVM_CONFIG for
    // the host platform. This only really works if the host LLVM and target
    // LLVM are compiled the same way, but for us that's typically the case.
    //
    // We *want* detect this cross compiling situation by asking llvm-config
    // what its host-target is. If that's not the TARGET, then we're cross
    // compiling. Unfortunately `llvm-config` seems either be buggy, or we're
    // misconfiguring it, because the `i686-pc-windows-gnu` build of LLVM will
    // report itself with a `--host-target` of `x86_64-pc-windows-gnu`. This
    // tricks us into thinking we're doing a cross build when we aren't, so
    // havoc ensues.
    //
    // In any case, if we're cross compiling, this generally just means that we
    // can't trust all the output of llvm-config because it might be targeted
    // for the host rather than the target. As a result a bunch of blocks below
    // are gated on `if !is_crossed`
    let target = env::var("TARGET").expect("TARGET was not set");
    let host = env::var("HOST").expect("HOST was not set");
    let is_crossed = target != host;

    let mut optional_components = vec![
        "x86",
        "arm",
        "aarch64",
        "amdgpu",
        "avr",
        "mips",
        "powerpc",
        "systemz",
        "jsbackend",
        "webassembly",
        "msp430",
        "sparc",
        "nvptx",
        "hexagon",
    ];

    let mut version_cmd = Command::new(&llvm_config);
    version_cmd.arg("--version");
    let version_output = output(&mut version_cmd);
    let mut parts = version_output.split('.').take(2).filter_map(|s| s.parse::<u32>().ok());
    let (major, _minor) = if let (Some(major), Some(minor)) = (parts.next(), parts.next()) {
        (major, minor)
    } else {
        (6, 0)
    };

    if major > 6 {
        optional_components.push("riscv");
    }

    let required_components = &[
        "ipo",
        "bitreader",
        "bitwriter",
        "linker",
        "asmparser",
        "lto",
        "coverage",
        "instrumentation",
    ];

    let components = output(Command::new(&llvm_config).arg("--components"));
    let mut components = components.split_whitespace().collect::<Vec<_>>();
    components.retain(|c| optional_components.contains(c) || required_components.contains(c));

    for component in required_components {
        if !components.contains(component) {
            panic!("require llvm component {} but wasn't found", component);
        }
    }

    for component in components.iter() {
        println!("cargo:rustc-cfg=llvm_component=\"{}\"", component);
    }

    if major >= 9 {
        println!("cargo:rustc-cfg=llvm_has_msp430_asm_parser");
    }

    // Link in our own LLVM shims, compiled with the same flags as LLVM
    let mut cmd = Command::new(&llvm_config);
    cmd.arg("--cxxflags");
    let cxxflags = output(&mut cmd);
    let mut cfg = cc::Build::new();
    cfg.warnings(false);
    for flag in cxxflags.split_whitespace() {
        // Ignore flags like `-m64` when we're doing a cross build
        if is_crossed && flag.starts_with("-m") {
            continue;
        }

        if flag.starts_with("-flto") {
            continue;
        }

        // -Wdate-time is not supported by the netbsd cross compiler
        if is_crossed && target.contains("netbsd") && flag.contains("date-time") {
            continue;
        }

        // Include path contains host directory, replace it with target
        if is_crossed && flag.starts_with("-I") {
            cfg.flag(&flag.replace(&host, &target));
            continue;
        }

        cfg.flag(flag);
    }

    for component in &components {
        let mut flag = String::from("LLVM_COMPONENT_");
        flag.push_str(&component.to_uppercase());
        cfg.define(&flag, None);
    }

    if tracked_env_var_os("LLVM_RUSTLLVM").is_some() {
        cfg.define("LLVM_RUSTLLVM", None);
    }

    if tracked_env_var_os("LLVM_NDEBUG").is_some() {
        cfg.define("NDEBUG", None);
        cfg.debug(false);
    }

    build_helper::rerun_if_changed_anything_in_dir(Path::new("../rustllvm"));
    cfg.file("../rustllvm/PassWrapper.cpp")
        .file("../rustllvm/RustWrapper.cpp")
        .file("../rustllvm/ArchiveWrapper.cpp")
        .file("../rustllvm/CoverageMappingWrapper.cpp")
        .file("../rustllvm/Linker.cpp")
        .cpp(true)
        .cpp_link_stdlib(None) // we handle this below
        .compile("rustllvm");

    let (llvm_kind, llvm_link_arg) = detect_llvm_link();

    // Link in all LLVM libraries, if we're using the "wrong" llvm-config then
    // we don't pick up system libs because unfortunately they're for the host
    // of llvm-config, not the target that we're attempting to link.
    let mut cmd = Command::new(&llvm_config);
    cmd.arg(llvm_link_arg).arg("--libs");

    if !is_crossed {
        cmd.arg("--system-libs");
    } else if target.contains("windows-gnu") {
        println!("cargo:rustc-link-lib=shell32");
        println!("cargo:rustc-link-lib=uuid");
    } else if target.contains("netbsd") {
        println!("cargo:rustc-link-lib=z");
    }
    cmd.args(&components);

    for lib in output(&mut cmd).split_whitespace() {
        let name = if lib.starts_with("-l") {
            &lib[2..]
        } else if lib.starts_with('-') {
            &lib[1..]
        } else if Path::new(lib).exists() {
            // On MSVC llvm-config will print the full name to libraries, but
            // we're only interested in the name part
            let name = Path::new(lib).file_name().unwrap().to_str().unwrap();
            name.trim_end_matches(".lib")
        } else if lib.ends_with(".lib") {
            // Some MSVC libraries just come up with `.lib` tacked on, so chop
            // that off
            lib.trim_end_matches(".lib")
        } else {
            continue;
        };

        // Don't need or want this library, but LLVM's CMake build system
        // doesn't provide a way to disable it, so filter it here even though we
        // may or may not have built it. We don't reference anything from this
        // library and it otherwise may just pull in extra dependencies on
        // libedit which we don't want
        if name == "LLVMLineEditor" {
            continue;
        }

        let kind = if name.starts_with("LLVM") { llvm_kind } else { "dylib" };
        println!("cargo:rustc-link-lib={}={}", kind, name);
    }

    // LLVM ldflags
    //
    // If we're a cross-compile of LLVM then unfortunately we can't trust these
    // ldflags (largely where all the LLVM libs are located). Currently just
    // hack around this by replacing the host triple with the target and pray
    // that those -L directories are the same!
    let mut cmd = Command::new(&llvm_config);
    cmd.arg(llvm_link_arg).arg("--ldflags");
    for lib in output(&mut cmd).split_whitespace() {
        if is_crossed {
            if lib.starts_with("-LIBPATH:") {
                println!("cargo:rustc-link-search=native={}", lib[9..].replace(&host, &target));
            } else if lib.starts_with("-L") {
                println!("cargo:rustc-link-search=native={}", lib[2..].replace(&host, &target));
            }
        } else if lib.starts_with("-LIBPATH:") {
            println!("cargo:rustc-link-search=native={}", &lib[9..]);
        } else if lib.starts_with("-l") {
            println!("cargo:rustc-link-lib={}", &lib[2..]);
        } else if lib.starts_with("-L") {
            println!("cargo:rustc-link-search=native={}", &lib[2..]);
        }
    }

    // Some LLVM linker flags (-L and -l) may be needed even when linking
    // librustc_llvm, for example when using static libc++, we may need to
    // manually specify the library search path and -ldl -lpthread as link
    // dependencies.
    let llvm_linker_flags = tracked_env_var_os("LLVM_LINKER_FLAGS");
    if let Some(s) = llvm_linker_flags {
        for lib in s.into_string().unwrap().split_whitespace() {
            if lib.starts_with("-l") {
                println!("cargo:rustc-link-lib={}", &lib[2..]);
            } else if lib.starts_with("-L") {
                println!("cargo:rustc-link-search=native={}", &lib[2..]);
            }
        }
    }

    let llvm_static_stdcpp = tracked_env_var_os("LLVM_STATIC_STDCPP");
    let llvm_use_libcxx = tracked_env_var_os("LLVM_USE_LIBCXX");

    let stdcppname = if target.contains("openbsd") {
        if target.contains("sparc64") { "estdc++" } else { "c++" }
    } else if target.contains("freebsd") {
        "c++"
    } else if target.contains("darwin") {
        "c++"
    } else if target.contains("netbsd") && llvm_static_stdcpp.is_some() {
        // NetBSD uses a separate library when relocation is required
        "stdc++_pic"
    } else if llvm_use_libcxx.is_some() {
        "c++"
    } else {
        "stdc++"
    };

    // RISC-V requires libatomic for sub-word atomic operations
    if target.starts_with("riscv") {
        println!("cargo:rustc-link-lib=atomic");
    }

    // C++ runtime library
    if !target.contains("msvc") {
        if let Some(s) = llvm_static_stdcpp {
            assert!(!cxxflags.contains("stdlib=libc++"));
            let path = PathBuf::from(s);
            println!("cargo:rustc-link-search=native={}", path.parent().unwrap().display());
            if target.contains("windows") {
                println!("cargo:rustc-link-lib=static-nobundle={}", stdcppname);
            } else {
                println!("cargo:rustc-link-lib=static={}", stdcppname);
            }
        } else if cxxflags.contains("stdlib=libc++") {
            println!("cargo:rustc-link-lib=c++");
        } else {
            println!("cargo:rustc-link-lib={}", stdcppname);
        }
    }

    // Libstdc++ depends on pthread which Rust doesn't link on MinGW
    // since nothing else requires it.
    if target.contains("windows-gnu") {
        println!("cargo:rustc-link-lib=static-nobundle=pthread");
    }
}
<commit_msg>Auto merge of #75655 - nielx:fix/haiku-llvm-libz, r=Mark-Simulacrum<commit_after>use std::env;
use std::path::{Path, PathBuf};
use std::process::Command;

use build_helper::{output, tracked_env_var_os};

fn detect_llvm_link() -> (&'static str, &'static str) {
    // Force the link mode we want, preferring static by default, but
    // possibly overridden by `configure --enable-llvm-link-shared`.
    if tracked_env_var_os("LLVM_LINK_SHARED").is_some() {
        ("dylib", "--link-shared")
    } else {
        ("static", "--link-static")
    }
}

fn main() {
    if tracked_env_var_os("RUST_CHECK").is_some() {
        // If we're just running `check`, there's no need for LLVM to be built.
        return;
    }

    build_helper::restore_library_path();

    let target = env::var("TARGET").expect("TARGET was not set");
    let llvm_config =
        tracked_env_var_os("LLVM_CONFIG").map(|x| Some(PathBuf::from(x))).unwrap_or_else(|| {
            if let Some(dir) = tracked_env_var_os("CARGO_TARGET_DIR").map(PathBuf::from) {
                let to_test = dir
                    .parent()
                    .unwrap()
                    .parent()
                    .unwrap()
                    .join(&target)
                    .join("llvm/bin/llvm-config");
                if Command::new(&to_test).output().is_ok() {
                    return Some(to_test);
                }
            }
            None
        });

    if let Some(llvm_config) = &llvm_config {
        println!("cargo:rerun-if-changed={}", llvm_config.display());
    }
    let llvm_config = llvm_config.unwrap_or_else(|| PathBuf::from("llvm-config"));

    // Test whether we're cross-compiling LLVM. This is a pretty rare case
    // currently where we're producing an LLVM for a different platform than
    // what this build script is currently running on.
    //
    // In that case, there's no guarantee that we can actually run the target,
    // so the build system works around this by giving us the LLVM_CONFIG for
    // the host platform. This only really works if the host LLVM and target
    // LLVM are compiled the same way, but for us that's typically the case.
    //
    // We *want* detect this cross compiling situation by asking llvm-config
    // what its host-target is. If that's not the TARGET, then we're cross
    // compiling. Unfortunately `llvm-config` seems either be buggy, or we're
    // misconfiguring it, because the `i686-pc-windows-gnu` build of LLVM will
    // report itself with a `--host-target` of `x86_64-pc-windows-gnu`. This
    // tricks us into thinking we're doing a cross build when we aren't, so
    // havoc ensues.
    //
    // In any case, if we're cross compiling, this generally just means that we
    // can't trust all the output of llvm-config because it might be targeted
    // for the host rather than the target. As a result a bunch of blocks below
    // are gated on `if !is_crossed`
    let target = env::var("TARGET").expect("TARGET was not set");
    let host = env::var("HOST").expect("HOST was not set");
    let is_crossed = target != host;

    let mut optional_components = vec![
        "x86",
        "arm",
        "aarch64",
        "amdgpu",
        "avr",
        "mips",
        "powerpc",
        "systemz",
        "jsbackend",
        "webassembly",
        "msp430",
        "sparc",
        "nvptx",
        "hexagon",
    ];

    let mut version_cmd = Command::new(&llvm_config);
    version_cmd.arg("--version");
    let version_output = output(&mut version_cmd);
    let mut parts = version_output.split('.').take(2).filter_map(|s| s.parse::<u32>().ok());
    let (major, _minor) = if let (Some(major), Some(minor)) = (parts.next(), parts.next()) {
        (major, minor)
    } else {
        (6, 0)
    };

    if major > 6 {
        optional_components.push("riscv");
    }

    let required_components = &[
        "ipo",
        "bitreader",
        "bitwriter",
        "linker",
        "asmparser",
        "lto",
        "coverage",
        "instrumentation",
    ];

    let components = output(Command::new(&llvm_config).arg("--components"));
    let mut components = components.split_whitespace().collect::<Vec<_>>();
    components.retain(|c| optional_components.contains(c) || required_components.contains(c));

    for component in required_components {
        if !components.contains(component) {
            panic!("require llvm component {} but wasn't found", component);
        }
    }

    for component in components.iter() {
        println!("cargo:rustc-cfg=llvm_component=\"{}\"", component);
    }

    if major >= 9 {
        println!("cargo:rustc-cfg=llvm_has_msp430_asm_parser");
    }

    // Link in our own LLVM shims, compiled with the same flags as LLVM
    let mut cmd = Command::new(&llvm_config);
    cmd.arg("--cxxflags");
    let cxxflags = output(&mut cmd);
    let mut cfg = cc::Build::new();
    cfg.warnings(false);
    for flag in cxxflags.split_whitespace() {
        // Ignore flags like `-m64` when we're doing a cross build
        if is_crossed && flag.starts_with("-m") {
            continue;
        }

        if flag.starts_with("-flto") {
            continue;
        }

        // -Wdate-time is not supported by the netbsd cross compiler
        if is_crossed && target.contains("netbsd") && flag.contains("date-time") {
            continue;
        }

        // Include path contains host directory, replace it with target
        if is_crossed && flag.starts_with("-I") {
            cfg.flag(&flag.replace(&host, &target));
            continue;
        }

        cfg.flag(flag);
    }

    for component in &components {
        let mut flag = String::from("LLVM_COMPONENT_");
        flag.push_str(&component.to_uppercase());
        cfg.define(&flag, None);
    }

    if tracked_env_var_os("LLVM_RUSTLLVM").is_some() {
        cfg.define("LLVM_RUSTLLVM", None);
    }

    if tracked_env_var_os("LLVM_NDEBUG").is_some() {
        cfg.define("NDEBUG", None);
        cfg.debug(false);
    }

    build_helper::rerun_if_changed_anything_in_dir(Path::new("../rustllvm"));
    cfg.file("../rustllvm/PassWrapper.cpp")
        .file("../rustllvm/RustWrapper.cpp")
        .file("../rustllvm/ArchiveWrapper.cpp")
        .file("../rustllvm/CoverageMappingWrapper.cpp")
        .file("../rustllvm/Linker.cpp")
        .cpp(true)
        .cpp_link_stdlib(None) // we handle this below
        .compile("rustllvm");

    let (llvm_kind, llvm_link_arg) = detect_llvm_link();

    // Link in all LLVM libraries, if we're using the "wrong" llvm-config then
    // we don't pick up system libs because unfortunately they're for the host
    // of llvm-config, not the target that we're attempting to link.
    let mut cmd = Command::new(&llvm_config);
    cmd.arg(llvm_link_arg).arg("--libs");

    if !is_crossed {
        cmd.arg("--system-libs");
    } else if target.contains("windows-gnu") {
        println!("cargo:rustc-link-lib=shell32");
        println!("cargo:rustc-link-lib=uuid");
    } else if target.contains("netbsd") || target.contains("haiku") {
        println!("cargo:rustc-link-lib=z");
    }
    cmd.args(&components);

    for lib in output(&mut cmd).split_whitespace() {
        let name = if lib.starts_with("-l") {
            &lib[2..]
        } else if lib.starts_with('-') {
            &lib[1..]
        } else if Path::new(lib).exists() {
            // On MSVC llvm-config will print the full name to libraries, but
            // we're only interested in the name part
            let name = Path::new(lib).file_name().unwrap().to_str().unwrap();
            name.trim_end_matches(".lib")
        } else if lib.ends_with(".lib") {
            // Some MSVC libraries just come up with `.lib` tacked on, so chop
            // that off
            lib.trim_end_matches(".lib")
        } else {
            continue;
        };

        // Don't need or want this library, but LLVM's CMake build system
        // doesn't provide a way to disable it, so filter it here even though we
        // may or may not have built it. We don't reference anything from this
        // library and it otherwise may just pull in extra dependencies on
        // libedit which we don't want
        if name == "LLVMLineEditor" {
            continue;
        }

        let kind = if name.starts_with("LLVM") { llvm_kind } else { "dylib" };
        println!("cargo:rustc-link-lib={}={}", kind, name);
    }

    // LLVM ldflags
    //
    // If we're a cross-compile of LLVM then unfortunately we can't trust these
    // ldflags (largely where all the LLVM libs are located). Currently just
    // hack around this by replacing the host triple with the target and pray
    // that those -L directories are the same!
    let mut cmd = Command::new(&llvm_config);
    cmd.arg(llvm_link_arg).arg("--ldflags");
    for lib in output(&mut cmd).split_whitespace() {
        if is_crossed {
            if lib.starts_with("-LIBPATH:") {
                println!("cargo:rustc-link-search=native={}", lib[9..].replace(&host, &target));
            } else if lib.starts_with("-L") {
                println!("cargo:rustc-link-search=native={}", lib[2..].replace(&host, &target));
            }
        } else if lib.starts_with("-LIBPATH:") {
            println!("cargo:rustc-link-search=native={}", &lib[9..]);
        } else if lib.starts_with("-l") {
            println!("cargo:rustc-link-lib={}", &lib[2..]);
        } else if lib.starts_with("-L") {
            println!("cargo:rustc-link-search=native={}", &lib[2..]);
        }
    }

    // Some LLVM linker flags (-L and -l) may be needed even when linking
    // librustc_llvm, for example when using static libc++, we may need to
    // manually specify the library search path and -ldl -lpthread as link
    // dependencies.
    let llvm_linker_flags = tracked_env_var_os("LLVM_LINKER_FLAGS");
    if let Some(s) = llvm_linker_flags {
        for lib in s.into_string().unwrap().split_whitespace() {
            if lib.starts_with("-l") {
                println!("cargo:rustc-link-lib={}", &lib[2..]);
            } else if lib.starts_with("-L") {
                println!("cargo:rustc-link-search=native={}", &lib[2..]);
            }
        }
    }

    let llvm_static_stdcpp = tracked_env_var_os("LLVM_STATIC_STDCPP");
    let llvm_use_libcxx = tracked_env_var_os("LLVM_USE_LIBCXX");

    let stdcppname = if target.contains("openbsd") {
        if target.contains("sparc64") { "estdc++" } else { "c++" }
    } else if target.contains("freebsd") {
        "c++"
    } else if target.contains("darwin") {
        "c++"
    } else if target.contains("netbsd") && llvm_static_stdcpp.is_some() {
        // NetBSD uses a separate library when relocation is required
        "stdc++_pic"
    } else if llvm_use_libcxx.is_some() {
        "c++"
    } else {
        "stdc++"
    };

    // RISC-V requires libatomic for sub-word atomic operations
    if target.starts_with("riscv") {
        println!("cargo:rustc-link-lib=atomic");
    }

    // C++ runtime library
    if !target.contains("msvc") {
        if let Some(s) = llvm_static_stdcpp {
            assert!(!cxxflags.contains("stdlib=libc++"));
            let path = PathBuf::from(s);
            println!("cargo:rustc-link-search=native={}", path.parent().unwrap().display());
            if target.contains("windows") {
                println!("cargo:rustc-link-lib=static-nobundle={}", stdcppname);
            } else {
                println!("cargo:rustc-link-lib=static={}", stdcppname);
            }
        } else if cxxflags.contains("stdlib=libc++") {
            println!("cargo:rustc-link-lib=c++");
        } else {
            println!("cargo:rustc-link-lib={}", stdcppname);
        }
    }

    // Libstdc++ depends on pthread which Rust doesn't link on MinGW
    // since nothing else requires it.
    if target.contains("windows-gnu") {
        println!("cargo:rustc-link-lib=static-nobundle=pthread");
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

<%namespace name="helpers" file="/helpers.mako.rs" />
<% from data import Keyword %>
<% data.new_style_struct("InheritedText", inherited=True, gecko_name="Text") %>

${helpers.predefined_type(
    "color",
    "ColorPropertyValue",
    "::cssparser::RGBA::new(0, 0, 0, 255)",
    animation_value_type="AnimatedRGBA",
    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
    ignored_when_colors_disabled="True",
    spec="https://drafts.csswg.org/css-color/#color",
)}

${helpers.predefined_type(
    "line-height",
    "LineHeight",
    "computed::LineHeight::normal()",
    animation_value_type="LineHeight",
    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE \
           APPLIES_TO_PLACEHOLDER GETCS_NEEDS_LAYOUT_FLUSH",
    spec="https://drafts.csswg.org/css2/visudet.html#propdef-line-height",
    servo_restyle_damage="reflow"
)}

// CSS Text Module Level 3

${helpers.predefined_type(
    "text-transform",
    "TextTransform",
    "computed::TextTransform::none()",
    animation_value_type="discrete",
    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
    spec="https://drafts.csswg.org/css-text/#propdef-text-transform",
    servo_restyle_damage="rebuild_and_reflow",
)}

${helpers.single_keyword(
    "hyphens",
    "manual none auto",
    gecko_enum_prefix="StyleHyphens",
    products="gecko",
    animation_value_type="discrete",
    extra_prefixes="moz",
    spec="https://drafts.csswg.org/css-text/#propdef-hyphens",
)}

// TODO: Support <percentage>
${helpers.single_keyword(
    "-moz-text-size-adjust",
    "auto none",
    gecko_constant_prefix="NS_STYLE_TEXT_SIZE_ADJUST",
    gecko_ffi_name="mTextSizeAdjust",
    products="gecko", animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-size-adjust/#adjustment-control",
    alias="-webkit-text-size-adjust",
)}

${helpers.predefined_type(
    "text-indent",
    "LengthPercentage",
    "computed::LengthPercentage::zero()",
    animation_value_type="ComputedValue",
    spec="https://drafts.csswg.org/css-text/#propdef-text-indent",
    allow_quirks="Yes",
    servo_restyle_damage = "reflow",
)}

// Also known as "word-wrap" (which is more popular because of IE), but this is
// the preferred name per CSS-TEXT 6.2.
${helpers.predefined_type(
    "overflow-wrap",
    "OverflowWrap",
    "computed::OverflowWrap::Normal",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-text/#propdef-overflow-wrap",
    alias="word-wrap",
    needs_context=False,
    servo_restyle_damage="rebuild_and_reflow",
)}

${helpers.predefined_type(
    "word-break",
    "WordBreak",
    "computed::WordBreak::Normal",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-text/#propdef-word-break",
    needs_context=False,
    servo_restyle_damage="rebuild_and_reflow",
)}

// TODO(pcwalton): Support `text-justify: distribute`.
<%helpers:single_keyword
    name="text-justify"
    values="auto none inter-word"
    extra_gecko_values="inter-character"
    extra_specified="${'distribute' if product == 'gecko' else ''}"
    gecko_enum_prefix="StyleTextJustify"
    animation_value_type="discrete"
    gecko_pref="layout.css.text-justify.enabled"
    flags="APPLIES_TO_PLACEHOLDER",
    spec="https://drafts.csswg.org/css-text/#propdef-text-justify"
    servo_restyle_damage="rebuild_and_reflow"
>
    % if product == 'gecko':
    impl ToComputedValue for SpecifiedValue {
        type ComputedValue = computed_value::T;

        #[inline]
        fn to_computed_value(&self, _: &Context) -> computed_value::T {
            match *self {
                % for value in "Auto None InterCharacter InterWord".split():
                SpecifiedValue::${value} => computed_value::T::${value},
                % endfor
                // https://drafts.csswg.org/css-text-3/#valdef-text-justify-distribute
                SpecifiedValue::Distribute => computed_value::T::InterCharacter,
            }
        }

        #[inline]
        fn from_computed_value(computed: &computed_value::T) -> SpecifiedValue {
            match *computed {
                % for value in "Auto None InterCharacter InterWord".split():
                computed_value::T::${value} => SpecifiedValue::${value},
                % endfor
            }
        }
    }
    % endif
</%helpers:single_keyword>

${helpers.single_keyword(
    "text-align-last",
    "auto start end left right center justify",
    products="gecko",
    gecko_constant_prefix="NS_STYLE_TEXT_ALIGN",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-text/#propdef-text-align-last",
)}

// TODO make this a shorthand and implement text-align-last/text-align-all
${helpers.predefined_type(
    "text-align",
    "TextAlign",
    "computed::TextAlign::Start",
    animation_value_type="discrete",
    flags="APPLIES_TO_PLACEHOLDER",
    spec="https://drafts.csswg.org/css-text/#propdef-text-align",
    servo_restyle_damage = "reflow",
)}

${helpers.predefined_type(
    "letter-spacing",
    "LetterSpacing",
    "computed::LetterSpacing::normal()",
    animation_value_type="ComputedValue",
    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
    spec="https://drafts.csswg.org/css-text/#propdef-letter-spacing",
    servo_restyle_damage="rebuild_and_reflow",
)}

${helpers.predefined_type(
    "word-spacing",
    "WordSpacing",
    "computed::WordSpacing::zero()",
    animation_value_type="ComputedValue",
    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
    spec="https://drafts.csswg.org/css-text/#propdef-word-spacing",
    servo_restyle_damage="rebuild_and_reflow",
)}

<%helpers:single_keyword
    name="white-space"
    values="normal pre nowrap pre-wrap pre-line break-spaces"
    extra_gecko_values="-moz-pre-space"
    gecko_enum_prefix="StyleWhiteSpace"
    needs_conversion="True"
    animation_value_type="discrete"
    // Only allowed for UA sheets, which set it !important.
    flags="APPLIES_TO_PLACEHOLDER"
    spec="https://drafts.csswg.org/css-text/#propdef-white-space"
    servo_restyle_damage="rebuild_and_reflow"
>
    % if product != "gecko":
    impl SpecifiedValue {
        pub fn allow_wrap(&self) -> bool {
            match *self {
                SpecifiedValue::Nowrap |
                SpecifiedValue::Pre => false,
                SpecifiedValue::Normal |
                SpecifiedValue::PreWrap |
                SpecifiedValue::PreLine |
                SpecifiedValue::BreakSpaces => true,
            }
        }

        pub fn preserve_newlines(&self) -> bool {
            match *self {
                SpecifiedValue::Normal |
                SpecifiedValue::Nowrap => false,
                SpecifiedValue::Pre |
                SpecifiedValue::PreWrap |
                SpecifiedValue::PreLine |
                SpecifiedValue::BreakSpaces => true,
            }
        }

        pub fn preserve_spaces(&self) -> bool {
            match *self {
                SpecifiedValue::Normal |
                SpecifiedValue::Nowrap |
                SpecifiedValue::PreLine => false,
                SpecifiedValue::Pre |
                SpecifiedValue::PreWrap |
                SpecifiedValue::BreakSpaces => true,
            }
        }
    }
    % endif
</%helpers:single_keyword>

${helpers.predefined_type(
    "text-shadow",
    "SimpleShadow",
    None,
    vector=True,
    vector_animation_type="with_zero",
    animation_value_type="AnimatedTextShadowList",
    ignored_when_colors_disabled=True,
    simple_vector_bindings=True,
    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
    spec="https://drafts.csswg.org/css-text-decor-3/#text-shadow-property",
)}

${helpers.predefined_type(
    "text-emphasis-style",
    "TextEmphasisStyle",
    None,
    initial_specified_value="SpecifiedValue::None",
    products="gecko",
    boxed=True,
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-style",
)}

${helpers.predefined_type(
    "text-emphasis-position",
    "TextEmphasisPosition",
    "computed::TextEmphasisPosition::over_right()",
    initial_specified_value="specified::TextEmphasisPosition::over_right()",
    products="gecko",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-position",
)}

${helpers.predefined_type(
    "text-emphasis-color",
    "Color",
    "computed_value::T::currentcolor()",
    initial_specified_value="specified::Color::currentcolor()",
    products="gecko",
    animation_value_type="AnimatedColor",
    ignored_when_colors_disabled=True,
    spec="https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-color",
)}

${helpers.predefined_type(
    "-moz-tab-size",
    "NonNegativeLengthOrNumber",
    "generics::length::LengthOrNumber::Number(From::from(8.0))",
    products="gecko",
    animation_value_type="LengthOrNumber",
    spec="https://drafts.csswg.org/css-text-3/#tab-size-property",
)}

${helpers.predefined_type(
    "line-break",
    "LineBreak",
    "computed::LineBreak::Auto",
    products="gecko",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-text-3/#line-break-property",
    needs_context=False,
)}

// CSS Compatibility
// https://compat.spec.whatwg.org
${helpers.predefined_type(
    "-webkit-text-fill-color",
    "Color",
    "computed_value::T::currentcolor()",
    products="gecko",
    animation_value_type="AnimatedColor",
    ignored_when_colors_disabled=True,
    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
    spec="https://compat.spec.whatwg.org/#the-webkit-text-fill-color",
)}

${helpers.predefined_type(
    "-webkit-text-stroke-color",
    "Color",
    "computed_value::T::currentcolor()",
    initial_specified_value="specified::Color::currentcolor()",
    products="gecko",
    animation_value_type="AnimatedColor",
    ignored_when_colors_disabled=True,
    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
    spec="https://compat.spec.whatwg.org/#the-webkit-text-stroke-color",
)}

${helpers.predefined_type(
    "-webkit-text-stroke-width",
    "BorderSideWidth",
    "crate::values::computed::NonNegativeLength::new(0.)",
    initial_specified_value="specified::BorderSideWidth::zero()",
    computed_type="crate::values::computed::NonNegativeLength",
    products="gecko",
    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
    spec="https://compat.spec.whatwg.org/#the-webkit-text-stroke-width",
    animation_value_type="discrete",
)}

// CSS Ruby Layout Module Level 1
// https://drafts.csswg.org/css-ruby/
${helpers.single_keyword(
    "ruby-align",
    "space-around start center space-between",
    products="gecko",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-ruby/#ruby-align-property",
)}

${helpers.single_keyword(
    "ruby-position",
    "over under",
    products="gecko",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-ruby/#ruby-position-property",
)}

// CSS Writing Modes Module Level 3
// https://drafts.csswg.org/css-writing-modes-3/

${helpers.single_keyword(
    "text-combine-upright",
    "none all",
    products="gecko",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-writing-modes-3/#text-combine-upright",
)}

// SVG 1.1: Section 11 - Painting: Filling, Stroking and Marker Symbols
${helpers.single_keyword(
    "text-rendering",
    "auto optimizespeed optimizelegibility geometricprecision",
    gecko_enum_prefix="StyleTextRendering",
    animation_value_type="discrete",
    spec="https://www.w3.org/TR/SVG11/painting.html#TextRenderingProperty",
    servo_restyle_damage="rebuild_and_reflow",
)}

// FIXME Firefox expects the initial value of this property to change depending
// on the value of the layout.css.control-characters.visible pref.
${helpers.single_keyword(
    "-moz-control-character-visibility",
    "hidden visible",
    gecko_constant_prefix="NS_STYLE_CONTROL_CHARACTER_VISIBILITY",
    animation_value_type="none",
    gecko_ffi_name="mControlCharacterVisibility",
    products="gecko",
    spec="Nonstandard",
)}

// text underline offset
${helpers.predefined_type(
    "text-underline-offset",
    "LengthOrAuto",
    "computed::LengthOrAuto::auto()",
    products="gecko",
    animation_value_type="ComputedValue",
    gecko_pref="layout.css.text-underline-offset.enabled",
    spec="https://drafts.csswg.org/css-text-decor-4/#underline-offset",
)}
<commit_msg>style: Servo doesn't implement white-space: break-spaces.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

<%namespace name="helpers" file="/helpers.mako.rs" />
<% from data import Keyword %>
<% data.new_style_struct("InheritedText", inherited=True, gecko_name="Text") %>

${helpers.predefined_type(
    "color",
    "ColorPropertyValue",
    "::cssparser::RGBA::new(0, 0, 0, 255)",
    animation_value_type="AnimatedRGBA",
    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
    ignored_when_colors_disabled="True",
    spec="https://drafts.csswg.org/css-color/#color",
)}

${helpers.predefined_type(
    "line-height",
    "LineHeight",
    "computed::LineHeight::normal()",
    animation_value_type="LineHeight",
    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE \
           APPLIES_TO_PLACEHOLDER GETCS_NEEDS_LAYOUT_FLUSH",
    spec="https://drafts.csswg.org/css2/visudet.html#propdef-line-height",
    servo_restyle_damage="reflow"
)}

// CSS Text Module Level 3

${helpers.predefined_type(
    "text-transform",
    "TextTransform",
    "computed::TextTransform::none()",
    animation_value_type="discrete",
    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
    spec="https://drafts.csswg.org/css-text/#propdef-text-transform",
    servo_restyle_damage="rebuild_and_reflow",
)}

${helpers.single_keyword(
    "hyphens",
    "manual none auto",
    gecko_enum_prefix="StyleHyphens",
    products="gecko",
    animation_value_type="discrete",
    extra_prefixes="moz",
    spec="https://drafts.csswg.org/css-text/#propdef-hyphens",
)}

// TODO: Support <percentage>
${helpers.single_keyword(
    "-moz-text-size-adjust",
    "auto none",
    gecko_constant_prefix="NS_STYLE_TEXT_SIZE_ADJUST",
    gecko_ffi_name="mTextSizeAdjust",
    products="gecko", animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-size-adjust/#adjustment-control",
    alias="-webkit-text-size-adjust",
)}

${helpers.predefined_type(
    "text-indent",
    "LengthPercentage",
    "computed::LengthPercentage::zero()",
    animation_value_type="ComputedValue",
    spec="https://drafts.csswg.org/css-text/#propdef-text-indent",
    allow_quirks="Yes",
    servo_restyle_damage = "reflow",
)}

// Also known as "word-wrap" (which is more popular because of IE), but this is
// the preferred name per CSS-TEXT 6.2.
${helpers.predefined_type(
    "overflow-wrap",
    "OverflowWrap",
    "computed::OverflowWrap::Normal",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-text/#propdef-overflow-wrap",
    alias="word-wrap",
    needs_context=False,
    servo_restyle_damage="rebuild_and_reflow",
)}

${helpers.predefined_type(
    "word-break",
    "WordBreak",
    "computed::WordBreak::Normal",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-text/#propdef-word-break",
    needs_context=False,
    servo_restyle_damage="rebuild_and_reflow",
)}

// TODO(pcwalton): Support `text-justify: distribute`.
<%helpers:single_keyword
    name="text-justify"
    values="auto none inter-word"
    extra_gecko_values="inter-character"
    extra_specified="${'distribute' if product == 'gecko' else ''}"
    gecko_enum_prefix="StyleTextJustify"
    animation_value_type="discrete"
    gecko_pref="layout.css.text-justify.enabled"
    flags="APPLIES_TO_PLACEHOLDER",
    spec="https://drafts.csswg.org/css-text/#propdef-text-justify"
    servo_restyle_damage="rebuild_and_reflow"
>
    % if product == 'gecko':
    impl ToComputedValue for SpecifiedValue {
        type ComputedValue = computed_value::T;

        #[inline]
        fn to_computed_value(&self, _: &Context) -> computed_value::T {
            match *self {
                % for value in "Auto None InterCharacter InterWord".split():
                SpecifiedValue::${value} => computed_value::T::${value},
                % endfor
                // https://drafts.csswg.org/css-text-3/#valdef-text-justify-distribute
                SpecifiedValue::Distribute => computed_value::T::InterCharacter,
            }
        }

        #[inline]
        fn from_computed_value(computed: &computed_value::T) -> SpecifiedValue {
            match *computed {
                % for value in "Auto None InterCharacter InterWord".split():
                computed_value::T::${value} => SpecifiedValue::${value},
                % endfor
            }
        }
    }
    % endif
</%helpers:single_keyword>

${helpers.single_keyword(
    "text-align-last",
    "auto start end left right center justify",
    products="gecko",
    gecko_constant_prefix="NS_STYLE_TEXT_ALIGN",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-text/#propdef-text-align-last",
)}

// TODO make this a shorthand and implement text-align-last/text-align-all
${helpers.predefined_type(
    "text-align",
    "TextAlign",
    "computed::TextAlign::Start",
    animation_value_type="discrete",
    flags="APPLIES_TO_PLACEHOLDER",
    spec="https://drafts.csswg.org/css-text/#propdef-text-align",
    servo_restyle_damage = "reflow",
)}

${helpers.predefined_type(
    "letter-spacing",
    "LetterSpacing",
    "computed::LetterSpacing::normal()",
    animation_value_type="ComputedValue",
    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
    spec="https://drafts.csswg.org/css-text/#propdef-letter-spacing",
    servo_restyle_damage="rebuild_and_reflow",
)}

${helpers.predefined_type(
    "word-spacing",
    "WordSpacing",
    "computed::WordSpacing::zero()",
    animation_value_type="ComputedValue",
    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
    spec="https://drafts.csswg.org/css-text/#propdef-word-spacing",
    servo_restyle_damage="rebuild_and_reflow",
)}

<%helpers:single_keyword
    name="white-space"
    values="normal pre nowrap pre-wrap pre-line"
    extra_gecko_values="break-spaces -moz-pre-space"
    gecko_enum_prefix="StyleWhiteSpace"
    needs_conversion="True"
    animation_value_type="discrete"
    // Only allowed for UA sheets, which set it !important.
    flags="APPLIES_TO_PLACEHOLDER"
    spec="https://drafts.csswg.org/css-text/#propdef-white-space"
    servo_restyle_damage="rebuild_and_reflow"
>
    % if product != "gecko":
    impl SpecifiedValue {
        pub fn allow_wrap(&self) -> bool {
            match *self {
                SpecifiedValue::Nowrap |
                SpecifiedValue::Pre => false,
                SpecifiedValue::Normal |
                SpecifiedValue::PreWrap |
                SpecifiedValue::PreLine => true,
            }
        }

        pub fn preserve_newlines(&self) -> bool {
            match *self {
                SpecifiedValue::Normal |
                SpecifiedValue::Nowrap => false,
                SpecifiedValue::Pre |
                SpecifiedValue::PreWrap |
                SpecifiedValue::PreLine => true,
            }
        }

        pub fn preserve_spaces(&self) -> bool {
            match *self {
                SpecifiedValue::Normal |
                SpecifiedValue::Nowrap |
                SpecifiedValue::PreLine => false,
                SpecifiedValue::Pre |
                SpecifiedValue::PreWrap => true,
            }
        }
    }
    % endif
</%helpers:single_keyword>

${helpers.predefined_type(
    "text-shadow",
    "SimpleShadow",
    None,
    vector=True,
    vector_animation_type="with_zero",
    animation_value_type="AnimatedTextShadowList",
    ignored_when_colors_disabled=True,
    simple_vector_bindings=True,
    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
    spec="https://drafts.csswg.org/css-text-decor-3/#text-shadow-property",
)}

${helpers.predefined_type(
    "text-emphasis-style",
    "TextEmphasisStyle",
    None,
    initial_specified_value="SpecifiedValue::None",
    products="gecko",
    boxed=True,
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-style",
)}

${helpers.predefined_type(
    "text-emphasis-position",
    "TextEmphasisPosition",
    "computed::TextEmphasisPosition::over_right()",
    initial_specified_value="specified::TextEmphasisPosition::over_right()",
    products="gecko",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-position",
)}

${helpers.predefined_type(
    "text-emphasis-color",
    "Color",
    "computed_value::T::currentcolor()",
    initial_specified_value="specified::Color::currentcolor()",
    products="gecko",
    animation_value_type="AnimatedColor",
    ignored_when_colors_disabled=True,
    spec="https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-color",
)}

${helpers.predefined_type(
    "-moz-tab-size",
    "NonNegativeLengthOrNumber",
    "generics::length::LengthOrNumber::Number(From::from(8.0))",
    products="gecko",
    animation_value_type="LengthOrNumber",
    spec="https://drafts.csswg.org/css-text-3/#tab-size-property",
)}

${helpers.predefined_type(
    "line-break",
    "LineBreak",
    "computed::LineBreak::Auto",
    products="gecko",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-text-3/#line-break-property",
    needs_context=False,
)}

// CSS Compatibility
// https://compat.spec.whatwg.org
${helpers.predefined_type(
    "-webkit-text-fill-color",
    "Color",
    "computed_value::T::currentcolor()",
    products="gecko",
    animation_value_type="AnimatedColor",
    ignored_when_colors_disabled=True,
    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
    spec="https://compat.spec.whatwg.org/#the-webkit-text-fill-color",
)}

${helpers.predefined_type(
    "-webkit-text-stroke-color",
    "Color",
    "computed_value::T::currentcolor()",
    initial_specified_value="specified::Color::currentcolor()",
    products="gecko",
    animation_value_type="AnimatedColor",
    ignored_when_colors_disabled=True,
    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
    spec="https://compat.spec.whatwg.org/#the-webkit-text-stroke-color",
)}

${helpers.predefined_type(
    "-webkit-text-stroke-width",
    "BorderSideWidth",
    "crate::values::computed::NonNegativeLength::new(0.)",
    initial_specified_value="specified::BorderSideWidth::zero()",
    computed_type="crate::values::computed::NonNegativeLength",
    products="gecko",
    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
    spec="https://compat.spec.whatwg.org/#the-webkit-text-stroke-width",
    animation_value_type="discrete",
)}

// CSS Ruby Layout Module Level 1
// https://drafts.csswg.org/css-ruby/
${helpers.single_keyword(
    "ruby-align",
    "space-around start center space-between",
    products="gecko",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-ruby/#ruby-align-property",
)}

${helpers.single_keyword(
    "ruby-position",
    "over under",
    products="gecko",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-ruby/#ruby-position-property",
)}

// CSS Writing Modes Module Level 3
// https://drafts.csswg.org/css-writing-modes-3/

${helpers.single_keyword(
    "text-combine-upright",
    "none all",
    products="gecko",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-writing-modes-3/#text-combine-upright",
)}

// SVG 1.1: Section 11 - Painting: Filling, Stroking and Marker Symbols
${helpers.single_keyword(
    "text-rendering",
    "auto optimizespeed optimizelegibility geometricprecision",
    gecko_enum_prefix="StyleTextRendering",
    animation_value_type="discrete",
    spec="https://www.w3.org/TR/SVG11/painting.html#TextRenderingProperty",
    servo_restyle_damage="rebuild_and_reflow",
)}

// FIXME Firefox expects the initial value of this property to change depending
// on the value of the layout.css.control-characters.visible pref.
${helpers.single_keyword(
    "-moz-control-character-visibility",
    "hidden visible",
    gecko_constant_prefix="NS_STYLE_CONTROL_CHARACTER_VISIBILITY",
    animation_value_type="none",
    gecko_ffi_name="mControlCharacterVisibility",
    products="gecko",
    spec="Nonstandard",
)}

// text underline offset
${helpers.predefined_type(
    "text-underline-offset",
    "LengthOrAuto",
    "computed::LengthOrAuto::auto()",
    products="gecko",
    animation_value_type="ComputedValue",
    gecko_pref="layout.css.text-underline-offset.enabled",
    spec="https://drafts.csswg.org/css-text-decor-4/#underline-offset",
)}
<|endoftext|>


<commit_before>
extern crate misc;
extern crate bson;
extern crate elmo;
extern crate storage_sqlite3;

use bson::BsonValue;

#[test]
fn just_connect() {
    fn f() -> elmo::Result<()> {
        let db = try!(storage_sqlite3::connect(&misc::tempfile("just_connect")));
        drop(db);
        Ok(())
    }
    let r = f();
    println!("{:?}", r);
    assert!(r.is_ok());
}

#[test]
fn prepare_write() {
    fn f() -> elmo::Result<()> {
        let db = try!(storage_sqlite3::connect(&misc::tempfile("prepare_write")));
        {
            let tx = try!(db.begin_write());
            {
                let _p = try!(tx.get_collection_writer("foo", "bar"));
            }
        }
        Ok(())
    }
    let r = f();
    println!("{:?}", r);
    assert!(r.is_ok());
}

#[test]
fn insert() {
    fn f() -> elmo::Result<()> {
        let db = try!(storage_sqlite3::connect(&misc::tempfile("insert")));
        {
            let tx = try!(db.begin_write());
            {
                let mut p = try!(tx.get_collection_writer("foo", "bar"));

                let mut pairs = Vec::new();
                pairs.push((String::from("_id"), BsonValue::BString(misc::tid())));
                pairs.push((String::from("ok"), BsonValue::BInt32(0)));
                let doc = BsonValue::BDocument(pairs);

                try!(p.insert(&doc));
            }
            try!(tx.commit());
        }
        drop(db);
        Ok(())
    }
    let r = f();
    println!("{:?}", r);
    assert!(r.is_ok());
}

<commit_msg>fix storage/sqlite3 tests for recent api changes<commit_after>
extern crate misc;
extern crate bson;
extern crate elmo;
extern crate elmo_sqlite3;

#[test]
fn just_connect() {
    fn f() -> elmo::Result<()> {
        let db = try!(elmo_sqlite3::connect(&misc::tempfile("just_connect")));
        drop(db);
        Ok(())
    }
    let r = f();
    println!("{:?}", r);
    assert!(r.is_ok());
}

#[test]
fn prepare_write() {
    fn f() -> elmo::Result<()> {
        let db = try!(elmo_sqlite3::connect(&misc::tempfile("prepare_write")));
        {
            let tx = try!(db.begin_write());
            {
                let _p = try!(tx.get_collection_writer("foo", "bar"));
            }
        }
        Ok(())
    }
    let r = f();
    println!("{:?}", r);
    assert!(r.is_ok());
}

#[test]
fn insert() {
    fn f() -> elmo::Result<()> {
        let db = try!(elmo_sqlite3::connect(&misc::tempfile("insert")));
        {
            let tx = try!(db.begin_write());
            {
                let mut p = try!(tx.get_collection_writer("foo", "bar"));

                let mut doc = bson::Document::new_empty();
                doc.set_string("_id", misc::tid());
                doc.set_i32("ok", 0);

                try!(p.insert(&doc));
            }
            try!(tx.commit());
        }
        drop(db);
        Ok(())
    }
    let r = f();
    println!("{:?}", r);
    assert!(r.is_ok());
}

<|endoftext|>