<commit_before>//! Track which values are live in an EBB with instruction granularity.
//!
//! The `LiveValueTracker` keeps track of the set of live SSA values at each instruction in an EBB.
//! The sets of live values are computed on the fly as the tracker is moved from instruction to
//! instruction, starting at the EBB header.

use dominator_tree::DominatorTree;
use entity_list::{EntityList, ListPool};
use ir::instructions::BranchInfo;
use ir::{Inst, Ebb, Value, DataFlowGraph, ProgramOrder, ExpandedProgramPoint};
use partition_slice::partition_slice;
use regalloc::affinity::Affinity;
use regalloc::liveness::Liveness;

use std::collections::HashMap;

type ValueList = EntityList<Value>;

/// Compute and track live values throughout an EBB.
pub struct LiveValueTracker {
    /// The set of values that are live at the current program point.
    live: LiveValueVec,

    /// Saved set of live values for every jump and branch that can potentially be an immediate
    /// dominator of an EBB.
    ///
    /// This is the set of values that are live *before* the branch.
    idom_sets: HashMap<Inst, ValueList>,

    /// Memory pool for the live sets.
    idom_pool: ListPool<Value>,
}

/// Information about a value that is live at the current program point.
pub struct LiveValue {
    /// The live value.
    pub value: Value,

    /// The local ending point of the live range in the current EBB, as returned by
    /// `LiveRange::def_local_end()` or `LiveRange::livein_local_end()`.
    pub endpoint: Inst,

    /// The affinity of the value as represented in its `LiveRange`.
    ///
    /// This value is simply a copy of the affinity stored in the live range. We copy it because
    /// almost all users of `LiveValue` need to look at it.
    pub affinity: Affinity,
}

struct LiveValueVec {
    /// The set of values that are live at the current program point.
    values: Vec<LiveValue>,

    /// How many values at the front of `values` are known to be live after `inst`?
    ///
    /// This is used to pass a much smaller slice to `partition_slice` when its called a second
    /// time for the same instruction.
    live_prefix: Option<(Inst, usize)>,
}

impl LiveValueVec {
    fn new() -> LiveValueVec {
        LiveValueVec {
            values: Vec::new(),
            live_prefix: None,
        }
    }

    /// Add a new live value to `values`.
    fn push(&mut self, value: Value, endpoint: Inst, affinity: Affinity) {
        self.values
            .push(LiveValue {
                      value,
                      endpoint,
                      affinity,
                  });
    }

    /// Remove all elements.
    fn clear(&mut self) {
        self.values.clear();
        self.live_prefix = None;
    }

    /// Make sure that the values killed by `next_inst` are moved to the end of the `values`
    /// vector.
    ///
    /// Returns the number of values that will be live after `next_inst`.
    fn live_after(&mut self, next_inst: Inst) -> usize {
        // How many values at the front of the vector are already known to survive `next_inst`?
        // We don't need to pass this prefix to `partition_slice()`
        let keep = match self.live_prefix {
            Some((i, prefix)) if i == next_inst => prefix,
            _ => 0,
        };

        // Move the remaining surviving values to the front partition of the vector.
        let prefix = keep + partition_slice(&mut self.values[keep..], |v| v.endpoint != next_inst);

        // Remember the new prefix length in case we get called again for the same `next_inst`.
        self.live_prefix = Some((next_inst, prefix));
        prefix
    }

    /// Remove the values killed by `next_inst`.
    fn remove_kill_values(&mut self, next_inst: Inst) {
        let keep = self.live_after(next_inst);
        self.values.truncate(keep);
    }
}

impl LiveValueTracker {
    /// Create a new blank tracker.
    pub fn new() -> LiveValueTracker {
        LiveValueTracker {
            live: LiveValueVec::new(),
            idom_sets: HashMap::new(),
            idom_pool: ListPool::new(),
        }
    }

    /// Clear all cached information.
    pub fn clear(&mut self) {
        self.live.clear();
        self.idom_sets.clear();
        self.idom_pool.clear();
    }

    /// Get the set of currently live values.
    ///
    /// Between calls to `process_inst()` and `drop_dead()`, this includes both values killed and
    /// defined by the current instruction.
    pub fn live(&self) -> &[LiveValue] {
        &self.live.values
    }

    /// Move the current position to the top of `ebb`.
    ///
    /// This depends on the stored live value set at `ebb`'s immediate dominator, so that must have
    /// been visited first.
    ///
    /// Returns `(liveins, args)` as a pair of slices. The first slice is the set of live-in values
    /// from the immediate dominator. The second slice is the set of `ebb` arguments that are live.
    /// Dead arguments with no uses are ignored and not added to the set.
    pub fn ebb_top<PO: ProgramOrder>(&mut self,
                                     ebb: Ebb,
                                     dfg: &DataFlowGraph,
                                     liveness: &Liveness,
                                     program_order: &PO,
                                     domtree: &DominatorTree)
                                     -> (&[LiveValue], &[LiveValue]) {
        // Start over, compute the set of live values at the top of the EBB from two sources:
        //
        // 1. Values that were live before `ebb`'s immediate dominator, filtered for those that are
        //    actually live-in.
        // 2. Arguments to `ebb` that are not dead.
        //
        self.live.clear();

        // Compute the live-in values. Start by filtering the set of values that were live before
        // the immediate dominator. Just use the empty set if there's no immediate dominator (i.e.,
        // the entry block or an unreachable block).
        if let Some(idom) = domtree.idom(ebb) {
            // If the immediate dominator exits, we must have a stored list for it. This is a
            // requirement to the order EBBs are visited: All dominators must have been processed
            // before the current EBB.
            let idom_live_list = self.idom_sets
                .get(&idom)
                .expect("No stored live set for dominator");
            // Get just the values that are live-in to `ebb`.
            for &value in idom_live_list.as_slice(&self.idom_pool) {
                let lr = liveness
                    .get(value)
                    .expect("Immediate dominator value has no live range");

                // Check if this value is live-in here.
                if let Some(endpoint) = lr.livein_local_end(ebb, program_order) {
                    self.live.push(value, endpoint, lr.affinity);
                }
            }
        }

        // Now add all the live arguments to `ebb`.
        let first_arg = self.live.values.len();
        for &value in dfg.ebb_args(ebb) {
            let lr = liveness
                .get(value)
                .expect("EBB argument value has no live range");
            assert_eq!(lr.def(), ebb.into());
            match lr.def_local_end().into() {
                ExpandedProgramPoint::Inst(endpoint) => {
                    self.live.push(value, endpoint, lr.affinity);
                }
                ExpandedProgramPoint::Ebb(local_ebb) => {
                    // This is a dead EBB argument which is not even live into the first
                    // instruction in the EBB. We can ignore it.
                    assert_eq!(local_ebb,
                               ebb,
                               "EBB argument live range ends at wrong EBB header");
                }
            }
        }

        self.live.values.split_at(first_arg)
    }

    /// Prepare to move past `inst`.
    ///
    /// Determine the set of already live values that are killed by `inst`, and add the new defined
    /// values to the tracked set.
    ///
    /// Returns `(throughs, kills, defs)` as a tuple of slices:
    ///
    /// 1. The `throughs` slice is the set of live-through values that are neither defined nor
    ///    killed by the instruction.
    /// 2. The `kills` slice is the set of values that were live before the instruction and are
    ///    killed at the instruction. This does not include dead defs.
    /// 3. The `defs` slice is guaranteed to be in the same order as `inst`'s results, and includes
    ///    dead defines.
    ///
    /// The order of `throughs` and `kills` is arbitrary.
    ///
    /// The `drop_dead()` method must be called next to actually remove the dead values from the
    /// tracked set after the two returned slices are no longer needed.
    pub fn process_inst(&mut self,
                        inst: Inst,
                        dfg: &DataFlowGraph,
                        liveness: &Liveness)
                        -> (&[LiveValue], &[LiveValue], &[LiveValue]) {
        // Save a copy of the live values before any branches or jumps that could be somebody's
        // immediate dominator.
        match dfg[inst].analyze_branch(&dfg.value_lists) {
            BranchInfo::NotABranch => {}
            _ => self.save_idom_live_set(inst),
        }

        // Move killed values to the end of the vector.
        // Don't remove them yet, `drop_dead()` will do that.
        let first_kill = self.live.live_after(inst);

        // Add the values defined by `inst`.
        let first_def = self.live.values.len();
        for &value in dfg.inst_results(inst) {
            let lr = match liveness.get(value) {
                Some(lr) => lr,
                None => panic!("{} result {} has no live range", dfg[inst].opcode(), value),
            };
            assert_eq!(lr.def(), inst.into());
            match lr.def_local_end().into() {
                ExpandedProgramPoint::Inst(endpoint) => {
                    self.live.push(value, endpoint, lr.affinity);
                }
                ExpandedProgramPoint::Ebb(ebb) => {
                    panic!("Instruction result live range can't end at {}", ebb);
                }
            }
        }

        (&self.live.values[0..first_kill],
         &self.live.values[first_kill..first_def],
         &self.live.values[first_def..])
    }

    /// Drop the values that are now dead after moving past `inst`.
    ///
    /// This removes both live values that were killed by `inst` and dead defines on `inst` itself.
    ///
    /// This must be called after `process_inst(inst)` and before proceeding to the next
    /// instruction.
    pub fn drop_dead(&mut self, inst: Inst) {
        // Remove both live values that were killed by `inst` and dead defines from `inst`.
        self.live.remove_kill_values(inst);
    }

    /// Save the current set of live values so it is associated with `idom`.
    fn save_idom_live_set(&mut self, idom: Inst) {
        let values = self.live.values.iter().map(|lv| lv.value);
        let pool = &mut self.idom_pool;
        // If there already is a set saved for `idom`, just keep it.
        self.idom_sets
            .entry(idom)
            .or_insert_with(|| {
                                let mut list = ValueList::default();
                                list.extend(values, pool);
                                list
                            });
    }
}
<commit_msg>Propagate a few more LiveRange properties to LiveValue.<commit_after>//! Track which values are live in an EBB with instruction granularity.
//!
//! The `LiveValueTracker` keeps track of the set of live SSA values at each instruction in an EBB.
//! The sets of live values are computed on the fly as the tracker is moved from instruction to
//! instruction, starting at the EBB header.

use dominator_tree::DominatorTree;
use entity_list::{EntityList, ListPool};
use ir::instructions::BranchInfo;
use ir::{Inst, Ebb, Value, DataFlowGraph, ProgramOrder, ExpandedProgramPoint};
use partition_slice::partition_slice;
use regalloc::affinity::Affinity;
use regalloc::liveness::Liveness;
use regalloc::liverange::LiveRange;

use std::collections::HashMap;

type ValueList = EntityList<Value>;

/// Compute and track live values throughout an EBB.
pub struct LiveValueTracker {
    /// The set of values that are live at the current program point.
    live: LiveValueVec,

    /// Saved set of live values for every jump and branch that can potentially be an immediate
    /// dominator of an EBB.
    ///
    /// This is the set of values that are live *before* the branch.
    idom_sets: HashMap<Inst, ValueList>,

    /// Memory pool for the live sets.
    idom_pool: ListPool<Value>,
}

/// Information about a value that is live at the current program point.
pub struct LiveValue {
    /// The live value.
    pub value: Value,

    /// The local ending point of the live range in the current EBB, as returned by
    /// `LiveRange::def_local_end()` or `LiveRange::livein_local_end()`.
    pub endpoint: Inst,

    /// The affinity of the value as represented in its `LiveRange`.
    ///
    /// This value is simply a copy of the affinity stored in the live range. We copy it because
    /// almost all users of `LiveValue` need to look at it.
    pub affinity: Affinity,

    /// The live range for this value never leaves its EBB.
    pub is_local: bool,

    /// This value is dead - the live range ends immediately.
    pub is_dead: bool,
}

struct LiveValueVec {
    /// The set of values that are live at the current program point.
    values: Vec<LiveValue>,

    /// How many values at the front of `values` are known to be live after `inst`?
    ///
    /// This is used to pass a much smaller slice to `partition_slice` when its called a second
    /// time for the same instruction.
    live_prefix: Option<(Inst, usize)>,
}

impl LiveValueVec {
    fn new() -> LiveValueVec {
        LiveValueVec {
            values: Vec::new(),
            live_prefix: None,
        }
    }

    /// Add a new live value to `values`. Copy some properties from `lr`.
    fn push(&mut self, value: Value, endpoint: Inst, lr: &LiveRange) {
        self.values
            .push(LiveValue {
                      value,
                      endpoint,
                      affinity: lr.affinity,
                      is_local: lr.is_local(),
                      is_dead: lr.is_dead(),
                  });
    }

    /// Remove all elements.
    fn clear(&mut self) {
        self.values.clear();
        self.live_prefix = None;
    }

    /// Make sure that the values killed by `next_inst` are moved to the end of the `values`
    /// vector.
    ///
    /// Returns the number of values that will be live after `next_inst`.
    fn live_after(&mut self, next_inst: Inst) -> usize {
        // How many values at the front of the vector are already known to survive `next_inst`?
        // We don't need to pass this prefix to `partition_slice()`
        let keep = match self.live_prefix {
            Some((i, prefix)) if i == next_inst => prefix,
            _ => 0,
        };

        // Move the remaining surviving values to the front partition of the vector.
        let prefix = keep + partition_slice(&mut self.values[keep..], |v| v.endpoint != next_inst);

        // Remember the new prefix length in case we get called again for the same `next_inst`.
        self.live_prefix = Some((next_inst, prefix));
        prefix
    }

    /// Remove the values killed by `next_inst`.
    fn remove_kill_values(&mut self, next_inst: Inst) {
        let keep = self.live_after(next_inst);
        self.values.truncate(keep);
    }
}

impl LiveValueTracker {
    /// Create a new blank tracker.
    pub fn new() -> LiveValueTracker {
        LiveValueTracker {
            live: LiveValueVec::new(),
            idom_sets: HashMap::new(),
            idom_pool: ListPool::new(),
        }
    }

    /// Clear all cached information.
    pub fn clear(&mut self) {
        self.live.clear();
        self.idom_sets.clear();
        self.idom_pool.clear();
    }

    /// Get the set of currently live values.
    ///
    /// Between calls to `process_inst()` and `drop_dead()`, this includes both values killed and
    /// defined by the current instruction.
    pub fn live(&self) -> &[LiveValue] {
        &self.live.values
    }

    /// Move the current position to the top of `ebb`.
    ///
    /// This depends on the stored live value set at `ebb`'s immediate dominator, so that must have
    /// been visited first.
    ///
    /// Returns `(liveins, args)` as a pair of slices. The first slice is the set of live-in values
    /// from the immediate dominator. The second slice is the set of `ebb` arguments that are live.
    /// Dead arguments with no uses are ignored and not added to the set.
    pub fn ebb_top<PO: ProgramOrder>(&mut self,
                                     ebb: Ebb,
                                     dfg: &DataFlowGraph,
                                     liveness: &Liveness,
                                     program_order: &PO,
                                     domtree: &DominatorTree)
                                     -> (&[LiveValue], &[LiveValue]) {
        // Start over, compute the set of live values at the top of the EBB from two sources:
        //
        // 1. Values that were live before `ebb`'s immediate dominator, filtered for those that are
        //    actually live-in.
        // 2. Arguments to `ebb` that are not dead.
        //
        self.live.clear();

        // Compute the live-in values. Start by filtering the set of values that were live before
        // the immediate dominator. Just use the empty set if there's no immediate dominator (i.e.,
        // the entry block or an unreachable block).
        if let Some(idom) = domtree.idom(ebb) {
            // If the immediate dominator exits, we must have a stored list for it. This is a
            // requirement to the order EBBs are visited: All dominators must have been processed
            // before the current EBB.
            let idom_live_list = self.idom_sets
                .get(&idom)
                .expect("No stored live set for dominator");
            // Get just the values that are live-in to `ebb`.
            for &value in idom_live_list.as_slice(&self.idom_pool) {
                let lr = liveness
                    .get(value)
                    .expect("Immediate dominator value has no live range");

                // Check if this value is live-in here.
                if let Some(endpoint) = lr.livein_local_end(ebb, program_order) {
                    self.live.push(value, endpoint, lr);
                }
            }
        }

        // Now add all the live arguments to `ebb`.
        let first_arg = self.live.values.len();
        for &value in dfg.ebb_args(ebb) {
            let lr = liveness
                .get(value)
                .expect("EBB argument value has no live range");
            assert_eq!(lr.def(), ebb.into());
            match lr.def_local_end().into() {
                ExpandedProgramPoint::Inst(endpoint) => {
                    self.live.push(value, endpoint, lr);
                }
                ExpandedProgramPoint::Ebb(local_ebb) => {
                    // This is a dead EBB argument which is not even live into the first
                    // instruction in the EBB. We can ignore it.
                    assert_eq!(local_ebb,
                               ebb,
                               "EBB argument live range ends at wrong EBB header");
                }
            }
        }

        self.live.values.split_at(first_arg)
    }

    /// Prepare to move past `inst`.
    ///
    /// Determine the set of already live values that are killed by `inst`, and add the new defined
    /// values to the tracked set.
    ///
    /// Returns `(throughs, kills, defs)` as a tuple of slices:
    ///
    /// 1. The `throughs` slice is the set of live-through values that are neither defined nor
    ///    killed by the instruction.
    /// 2. The `kills` slice is the set of values that were live before the instruction and are
    ///    killed at the instruction. This does not include dead defs.
    /// 3. The `defs` slice is guaranteed to be in the same order as `inst`'s results, and includes
    ///    dead defines.
    ///
    /// The order of `throughs` and `kills` is arbitrary.
    ///
    /// The `drop_dead()` method must be called next to actually remove the dead values from the
    /// tracked set after the two returned slices are no longer needed.
    pub fn process_inst(&mut self,
                        inst: Inst,
                        dfg: &DataFlowGraph,
                        liveness: &Liveness)
                        -> (&[LiveValue], &[LiveValue], &[LiveValue]) {
        // Save a copy of the live values before any branches or jumps that could be somebody's
        // immediate dominator.
        match dfg[inst].analyze_branch(&dfg.value_lists) {
            BranchInfo::NotABranch => {}
            _ => self.save_idom_live_set(inst),
        }

        // Move killed values to the end of the vector.
        // Don't remove them yet, `drop_dead()` will do that.
        let first_kill = self.live.live_after(inst);

        // Add the values defined by `inst`.
        let first_def = self.live.values.len();
        for &value in dfg.inst_results(inst) {
            let lr = match liveness.get(value) {
                Some(lr) => lr,
                None => panic!("{} result {} has no live range", dfg[inst].opcode(), value),
            };
            assert_eq!(lr.def(), inst.into());
            match lr.def_local_end().into() {
                ExpandedProgramPoint::Inst(endpoint) => {
                    self.live.push(value, endpoint, lr);
                }
                ExpandedProgramPoint::Ebb(ebb) => {
                    panic!("Instruction result live range can't end at {}", ebb);
                }
            }
        }

        (&self.live.values[0..first_kill],
         &self.live.values[first_kill..first_def],
         &self.live.values[first_def..])
    }

    /// Drop the values that are now dead after moving past `inst`.
    ///
    /// This removes both live values that were killed by `inst` and dead defines on `inst` itself.
    ///
    /// This must be called after `process_inst(inst)` and before proceeding to the next
    /// instruction.
    pub fn drop_dead(&mut self, inst: Inst) {
        // Remove both live values that were killed by `inst` and dead defines from `inst`.
        self.live.remove_kill_values(inst);
    }

    /// Save the current set of live values so it is associated with `idom`.
    fn save_idom_live_set(&mut self, idom: Inst) {
        let values = self.live.values.iter().map(|lv| lv.value);
        let pool = &mut self.idom_pool;
        // If there already is a set saved for `idom`, just keep it.
        self.idom_sets
            .entry(idom)
            .or_insert_with(|| {
                                let mut list = ValueList::default();
                                list.extend(values, pool);
                                list
                            });
    }
}
<|endoftext|>


<commit_before>//! The [name (Naming)](https://docs.microsoft.com/en-us/typography/opentype/spec/name) table

use crate::FontData;

/// 'name'
pub const TAG: Tag = Tag::new(b"name");

include!("../../generated/generated_name.rs");

impl<'a> Name<'a> {
    /// The FontData containing the encoded name strings.
    pub(crate) fn string_data(&self) -> FontData<'a> {
        let base = self.offset_data();
        let off = self.storage_offset();
        base.split_off(off as usize).unwrap_or_default()
    }
}

impl NameRecord {
    /// Return a type that can decode the string data for this name entry.
    pub fn string<'a>(&self, data: FontData<'a>) -> Result<NameString<'a>, ReadError> {
        let start = self.string_offset().non_null().unwrap_or(0);
        let end = start + self.length() as usize;

        let data = data
            .as_bytes()
            .get(start..end)
            .ok_or(ReadError::OutOfBounds)?;

        let encoding = encoding(self.platform_id(), self.encoding_id());
        Ok(NameString { data, encoding })
    }
}

//-- all this is from pinot https://github.com/dfrg/pinot/blob/eff5239018ca50290fb890a84da3dd51505da364/src/name.rs
/// Entry for a name in the naming table.
///
/// This provides an iterator over characters.
#[derive(Copy, Clone)]
pub struct NameString<'a> {
    data: &'a [u8],
    encoding: Encoding,
}

impl<'a> NameString<'a> {
    /// An iterator over the `char`s in this name.
    pub fn chars(&self) -> CharIter<'a> {
        CharIter {
            data: self.data,
            encoding: self.encoding,
            pos: 0,
        }
    }
}

#[cfg(feature = "traversal")]
impl<'a> traversal::SomeString<'a> for NameString<'a> {
    fn iter_chars(&self) -> Box<dyn Iterator<Item = char> + 'a> {
        Box::new(self.into_iter())
    }
}

#[cfg(feature = "traversal")]
impl NameRecord {
    fn traverse_string<'a>(&self, data: FontData<'a>) -> traversal::FieldType<'a> {
        FieldType::StringOffset(traversal::StringOffset {
            offset: self.string_offset().into(),
            target: self.string(data).map(|s| Box::new(s) as _),
        })
    }
}

impl<'a> IntoIterator for NameString<'a> {
    type Item = char;
    type IntoIter = CharIter<'a>;
    fn into_iter(self) -> Self::IntoIter {
        self.chars()
    }
}

impl<'a> std::fmt::Display for NameString<'a> {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        for c in self.chars() {
            c.fmt(f)?;
        }
        Ok(())
    }
}

/// An iterator over the chars of a name record.
pub struct CharIter<'a> {
    data: &'a [u8],
    encoding: Encoding,
    pos: usize,
}

impl CharIter<'_> {
    fn bump_u16(&mut self) -> Option<u16> {
        let result = self
            .data
            .get(self.pos..self.pos + 2)
            .map(|x| u16::from_be_bytes(x.try_into().unwrap()))?;
        self.pos += 2;
        Some(result)
    }

    fn bump_u8(&mut self) -> Option<u8> {
        let result = self.data.get(self.pos)?;
        self.pos += 1;
        Some(*result)
    }
}

impl<'a> Iterator for CharIter<'a> {
    type Item = char;

    fn next(&mut self) -> Option<Self::Item> {
        if self.pos >= self.data.len() {
            return None;
        }
        let rep = core::char::REPLACEMENT_CHARACTER;
        let raw_c = match self.encoding {
            Encoding::Utf16Be => {
                let c1 = self.bump_u16()? as u32;
                if (0xD800..0xDC00).contains(&c1) {
                    let c2 = self.bump_u16()? as u32;
                    ((c1 & 0x3FF) << 10) + (c2 & 0x3FF) + 0x10000
                } else {
                    c1
                }
            }
            Encoding::MacRoman => {
                let c = self.bump_u8()? as u32;
                if c > 127 {
                    let idx = c as usize - 128;
                    MAC_ROMAN[idx] as u32
                } else {
                    c
                }
            }
            _ => return None,
        };
        Some(std::char::from_u32(raw_c).unwrap_or(rep))
    }
}

#[derive(Copy, Clone)]
enum Encoding {
    Utf16Be,
    MacRoman,
    Unknown,
}

fn encoding(platform_id: u16, encoding_id: u16) -> Encoding {
    match (platform_id, encoding_id) {
        (0, _) => Encoding::Utf16Be,
        (1, 0) => Encoding::MacRoman,
        (3, 0) => Encoding::Utf16Be,
        (3, 1) => Encoding::Utf16Be,
        (3, 10) => Encoding::Utf16Be,
        _ => Encoding::Unknown,
    }
}

#[rustfmt::skip]
const MAC_ROMAN: [u16; 128] = [
    196, 197, 199, 201, 209, 214, 220, 225, 224, 226, 228, 227, 229, 231, 233,
    232, 234, 235, 237, 236, 238, 239, 241, 243, 242, 244, 246, 245, 250, 249,
    251, 252, 8224, 176, 162, 163, 167, 8226, 182, 223, 174, 169, 8482, 180,
    168, 8800, 198, 216, 8734, 177, 8804, 8805, 165, 181, 8706, 8721, 8719,
    960, 8747, 170, 186, 937, 230, 248, 191, 161, 172, 8730, 402, 8776, 8710,
    171, 187, 8230, 160, 192, 195, 213, 338, 339, 8211, 8212, 8220, 8221, 8216,
    8217, 247, 9674, 255, 376, 8260, 8364, 8249, 8250, 64257, 64258, 8225, 183,
    8218, 8222, 8240, 194, 202, 193, 203, 200, 205, 206, 207, 204, 211, 212,
    63743, 210, 218, 219, 217, 305, 710, 732, 175, 728, 729, 730, 184, 733,
    731, 711,
];
<commit_msg>Tweak API for Encoding in name table<commit_after>//! The [name (Naming)](https://docs.microsoft.com/en-us/typography/opentype/spec/name) table

use crate::FontData;

/// 'name'
pub const TAG: Tag = Tag::new(b"name");

include!("../../generated/generated_name.rs");

impl<'a> Name<'a> {
    /// The FontData containing the encoded name strings.
    pub(crate) fn string_data(&self) -> FontData<'a> {
        let base = self.offset_data();
        let off = self.storage_offset();
        base.split_off(off as usize).unwrap_or_default()
    }
}

impl NameRecord {
    /// Return a type that can decode the string data for this name entry.
    pub fn string<'a>(&self, data: FontData<'a>) -> Result<NameString<'a>, ReadError> {
        let start = self.string_offset().non_null().unwrap_or(0);
        let end = start + self.length() as usize;

        let data = data
            .as_bytes()
            .get(start..end)
            .ok_or(ReadError::OutOfBounds)?;

        let encoding = Encoding::new(self.platform_id(), self.encoding_id());
        Ok(NameString { data, encoding })
    }
}

//-- all this is from pinot https://github.com/dfrg/pinot/blob/eff5239018ca50290fb890a84da3dd51505da364/src/name.rs
/// Encoded string for a name in the naming table.
///
/// This provides an iterator over characters.
#[derive(Copy, Clone)]
pub struct NameString<'a> {
    data: &'a [u8],
    encoding: Encoding,
}

impl<'a> NameString<'a> {
    /// An iterator over the `char`s in this name.
    pub fn chars(&self) -> CharIter<'a> {
        CharIter {
            data: self.data,
            encoding: self.encoding,
            pos: 0,
        }
    }
}

#[cfg(feature = "traversal")]
impl<'a> traversal::SomeString<'a> for NameString<'a> {
    fn iter_chars(&self) -> Box<dyn Iterator<Item = char> + 'a> {
        Box::new(self.into_iter())
    }
}

#[cfg(feature = "traversal")]
impl NameRecord {
    fn traverse_string<'a>(&self, data: FontData<'a>) -> traversal::FieldType<'a> {
        FieldType::StringOffset(traversal::StringOffset {
            offset: self.string_offset().into(),
            target: self.string(data).map(|s| Box::new(s) as _),
        })
    }
}

impl<'a> IntoIterator for NameString<'a> {
    type Item = char;
    type IntoIter = CharIter<'a>;
    fn into_iter(self) -> Self::IntoIter {
        self.chars()
    }
}

impl<'a> std::fmt::Display for NameString<'a> {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        for c in self.chars() {
            c.fmt(f)?;
        }
        Ok(())
    }
}

/// An iterator over the chars of a name record.
pub struct CharIter<'a> {
    data: &'a [u8],
    encoding: Encoding,
    pos: usize,
}

impl CharIter<'_> {
    fn bump_u16(&mut self) -> Option<u16> {
        let result = self
            .data
            .get(self.pos..self.pos + 2)
            .map(|x| u16::from_be_bytes(x.try_into().unwrap()))?;
        self.pos += 2;
        Some(result)
    }

    fn bump_u8(&mut self) -> Option<u8> {
        let result = self.data.get(self.pos)?;
        self.pos += 1;
        Some(*result)
    }
}

impl<'a> Iterator for CharIter<'a> {
    type Item = char;

    fn next(&mut self) -> Option<Self::Item> {
        if self.pos >= self.data.len() {
            return None;
        }
        let rep = core::char::REPLACEMENT_CHARACTER;
        let raw_c = match self.encoding {
            Encoding::Utf16Be => {
                let c1 = self.bump_u16()? as u32;
                if (0xD800..0xDC00).contains(&c1) {
                    let c2 = self.bump_u16()? as u32;
                    ((c1 & 0x3FF) << 10) + (c2 & 0x3FF) + 0x10000
                } else {
                    c1
                }
            }
            Encoding::MacRoman => {
                let c = self.bump_u8()? as u32;
                if c > 127 {
                    let idx = c as usize - 128;
                    MAC_ROMAN[idx] as u32
                } else {
                    c
                }
            }
            _ => return None,
        };
        Some(std::char::from_u32(raw_c).unwrap_or(rep))
    }
}

/// The encoding used by the name table.
#[derive(Copy, Clone)]
pub enum Encoding {
    Utf16Be,
    MacRoman,
    Unknown,
}

impl Encoding {
    /// Determine the coding from the platform and encoding id.
    pub fn new(platform_id: u16, encoding_id: u16) -> Encoding {
        match (platform_id, encoding_id) {
            (0, _) => Encoding::Utf16Be,
            (1, 0) => Encoding::MacRoman,
            (3, 0) => Encoding::Utf16Be,
            (3, 1) => Encoding::Utf16Be,
            (3, 10) => Encoding::Utf16Be,
            _ => Encoding::Unknown,
        }
    }
}

#[rustfmt::skip]
const MAC_ROMAN: [u16; 128] = [
    196, 197, 199, 201, 209, 214, 220, 225, 224, 226, 228, 227, 229, 231, 233,
    232, 234, 235, 237, 236, 238, 239, 241, 243, 242, 244, 246, 245, 250, 249,
    251, 252, 8224, 176, 162, 163, 167, 8226, 182, 223, 174, 169, 8482, 180,
    168, 8800, 198, 216, 8734, 177, 8804, 8805, 165, 181, 8706, 8721, 8719,
    960, 8747, 170, 186, 937, 230, 248, 191, 161, 172, 8730, 402, 8776, 8710,
    171, 187, 8230, 160, 192, 195, 213, 338, 339, 8211, 8212, 8220, 8221, 8216,
    8217, 247, 9674, 255, 376, 8260, 8364, 8249, 8250, 64257, 64258, 8225, 183,
    8218, 8222, 8240, 194, 202, 193, 203, 200, 205, 206, 207, 204, 211, 212,
    63743, 210, 218, 219, 217, 305, 710, 732, 175, 728, 729, 730, 184, 733,
    731, 711,
];
<|endoftext|>


<commit_before>use std::collections::btree_map::Iter;
use std::collections::BTreeMap;
use std::collections::HashMap;
use std::collections::HashSet;

use udis86::*;

use common::Graph;

#[derive(Clone)]
pub enum LinkType {
    Next,
    Branch,
    Call,
    SwitchCaseJump,
}

#[derive(Clone)]
pub struct Link {
    pub address: u64,
    pub target_address: u64,
    pub type_: LinkType,
}

pub struct ExtraData {
    pub function_address: u64,
    pub import_name: String,
    pub is_protected: bool,
}

#[allow(dead_code)]
pub struct InstructionGraph<'a> {
    disassembler: Disassembler,
    bytes: &'a [u8],
    first_address: u64,
    first_address_after_code: u64,
    instructions: BTreeMap<u64, Insn>,
    extra_data: HashMap<u64, ExtraData>,
    instruction_links: HashMap<u64, Vec<Link>>,
    reverse_links: HashMap<u64, Vec<Link>>,
    is_reversed: bool,
    edge_predicate: Box<Fn(&Link) -> bool>,
}

pub fn disassemble(bytes: &[u8], pc: u64) -> Result<InstructionGraph, String> {
    let mut disassembler = Disassembler::new(pc);

    let instructions = disassembler.disassemble(bytes, 0, bytes.len());
    let mut instruction_map = BTreeMap::new();

    for instr in instructions.into_iter() {
        instruction_map.insert(instr.address, instr);
    }
    let last_instruction = instruction_map.iter().next_back().unwrap().1;

    let graph = InstructionGraph {
        disassembler: disassembler,
        bytes: bytes,
        first_address: pc,
        first_address_after_code: last_instruction.address + (last_instruction.length as u64),
        instructions: instruction_map,
        extra_data: HashMap::new(),
        instruction_links: HashMap::new(),
        reverse_links: HashMap::new(),
        is_reversed: false,
        edge_predicate: Box::new(|_e| true),
    };

    Ok(graph)
}

impl<'a> Graph<u64, Insn, Link> for InstructionGraph<'a> {
    fn set_subgraph(&mut self, _subgraph: Option<HashSet<u64>>) -> &Self {
        panic!("NOT SUPPORTED");
    }

    fn set_edge_filter(&mut self, filter: Box<Fn(&Link) -> bool>) -> &Self {
        self.edge_predicate = filter;
        self
    }

    fn reverse_edges(&mut self) -> &Self {
        self.is_reversed = !self.is_reversed;
        self
    }

    fn contains(&self, vid: &u64) -> bool {
        self.instructions.contains_key(vid)
    }

    fn get_vertex(&self, vid: &u64) -> &Insn {
        self.instructions.get(vid).unwrap()
    }

    fn get_adjacent(&self, vid: &u64) -> Vec<Result<(&u64, &Link), String>> {
        let mut result = vec![];
        let instruction_links = if self.is_reversed {
            &self.reverse_links
        } else {
            &self.instruction_links
        };
        match instruction_links.get(vid) {
            Some(links) => for link in links.iter().filter(|e| (*self.edge_predicate)(e)) {
                let address = if self.is_reversed {
                    &link.address
                } else {
                    &link.target_address
                };
                if self.in_bounds(*address) {
                    result.push(Result::Ok((address, link)));
                } else {
                    result.push(Result::Err(format!(
                        "DFS: Jump outside of code section: {}",
                        address
                    )));
                }
            },
            None => result.push(Result::Err(format!(
                "DFS: Couldn't find links for {}",
                self.instructions.get(vid).unwrap().to_string()
            ))),
        }
        result
    }
}

impl<'a> InstructionGraph<'a> {
    pub fn instructions_iter(&self) -> Iter<u64, Insn> {
        self.instructions.iter()
    }

    pub fn first_address_after_code(&self) -> u64 {
        self.first_address_after_code
    }

    pub fn contains_address(&self, address: u64) -> bool {
        self.instructions.contains_key(&address)
    }

    pub fn contains(&self, instr: Insn) -> bool {
        false // self.instructions.contains_key(&instr.address())
    }

    pub fn in_bounds(&self, address: u64) -> bool {
        address >= self.first_address && address < self.first_address_after_code
    }

    pub fn get_next(&self, address: u64) -> u64 {
        match (address + 1..self.first_address_after_code - 1)
            .find(|a| self.instructions.contains_key(a))
        {
            Some(address) => address,
            None => panic!("Failed to find the next address."),
        }
    }

    pub fn get_in_value(&self, address: u64) -> u32 {
        self.reverse_links[&address].len() as u32
    }

    pub fn get_out_value(&self, address: u64) -> u32 {
        self.instruction_links[&address].len() as u32
    }

    // pub fn get_bytes(...)

    // pub fn get_extra_data(...)

    pub fn add_link(&mut self, offset: u64, target_offset: u64, type_: LinkType) -> () {
        let link = Link {
            address: offset,
            target_address: target_offset,
            type_,
        };

        let links = match self.instruction_links.get_mut(&offset) {
            Some(links) => links,
            None => {
                let links = Vec::new();
                self.instruction_links.insert(offset, links);
                self.instruction_links.get_mut(&offset).unwrap()
            }
        };
        links.push(link.clone());

        let reverse_links = match self.reverse_links.get_mut(&offset) {
            Some(links) => links,
            None => {
                let links = Vec::new();
                self.instruction_links.insert(offset, links);
                self.instruction_links.get_mut(&offset).unwrap()
            }
        };
        reverse_links.push(link);
    }

    // pub fn add_jump_table_entry(...)

    // pub fn add_jump_table_indirect_entries(...)

    // pub fn split_instruction_at(...)

    // pub fn read_u32(...)

    // pub fn redisassemble(...)

    // fn to_byte_array_index(...)
}
<commit_msg>Complete InstructionGraph implementation<commit_after>use std;
use std::collections::btree_map::Iter;
use std::collections::BTreeMap;
use std::collections::HashMap;
use std::collections::HashSet;

use libudis86_sys::{ud_mnemonic_code, ud_type};

use common::Graph;
use udis86::*;

#[derive(Clone)]
pub enum LinkType {
    Next,
    Branch,
    Call,
    SwitchCaseJump,
}

#[derive(Clone)]
pub struct Link {
    pub address: u64,
    pub target_address: u64,
    pub type_: LinkType,
}

pub struct ExtraData {
    pub function_address: u64,
    pub import_name: String,
    pub is_protected: bool,
}

#[allow(dead_code)]
pub struct InstructionGraph<'a> {
    disassembler: Disassembler,
    bytes: &'a [u8],
    first_address: u64,
    first_address_after_code: u64,
    instructions: BTreeMap<u64, Insn>,
    extra_data: HashMap<u64, ExtraData>,
    instruction_links: HashMap<u64, Vec<Link>>,
    reverse_links: HashMap<u64, Vec<Link>>,
    is_reversed: bool,
    edge_predicate: Box<Fn(&Link) -> bool>,
}

pub fn disassemble(bytes: &[u8], pc: u64) -> Result<InstructionGraph, String> {
    let mut disassembler = Disassembler::new(pc);

    let instructions = disassembler.disassemble(bytes, 0, bytes.len());
    let mut instruction_map = BTreeMap::new();

    for instr in instructions.into_iter() {
        instruction_map.insert(instr.address, instr);
    }
    let last_instruction = instruction_map.iter().next_back().unwrap().1;

    let graph = InstructionGraph {
        disassembler: disassembler,
        bytes: bytes,
        first_address: pc,
        first_address_after_code: last_instruction.address + (last_instruction.length as u64),
        instructions: instruction_map,
        extra_data: HashMap::new(),
        instruction_links: HashMap::new(),
        reverse_links: HashMap::new(),
        is_reversed: false,
        edge_predicate: Box::new(|_e| true),
    };

    Ok(graph)
}

impl<'a> Graph<u64, Insn, Link> for InstructionGraph<'a> {
    fn set_subgraph(&mut self, _subgraph: Option<HashSet<u64>>) -> &Self {
        panic!("NOT SUPPORTED");
    }

    fn set_edge_filter(&mut self, filter: Box<Fn(&Link) -> bool>) -> &Self {
        self.edge_predicate = filter;
        self
    }

    fn reverse_edges(&mut self) -> &Self {
        self.is_reversed = !self.is_reversed;
        self
    }

    fn contains(&self, vid: &u64) -> bool {
        self.instructions.contains_key(vid)
    }

    fn get_vertex(&self, vid: &u64) -> &Insn {
        self.instructions.get(vid).unwrap()
    }

    fn get_adjacent(&self, vid: &u64) -> Vec<Result<(&u64, &Link), String>> {
        let mut result = vec![];
        let instruction_links = if self.is_reversed {
            &self.reverse_links
        } else {
            &self.instruction_links
        };
        match instruction_links.get(vid) {
            Some(links) => for link in links.iter().filter(|e| (*self.edge_predicate)(e)) {
                let address = if self.is_reversed {
                    &link.address
                } else {
                    &link.target_address
                };
                if self.in_bounds(*address) {
                    result.push(Result::Ok((address, link)));
                } else {
                    result.push(Result::Err(format!(
                        "DFS: Jump outside of code section: {}",
                        address
                    )));
                }
            },
            None => result.push(Result::Err(format!(
                "DFS: Couldn't find links for {}",
                self.instructions.get(vid).unwrap().to_string()
            ))),
        }
        result
    }
}

impl<'a> InstructionGraph<'a> {
    pub fn instructions_iter(&self) -> Iter<u64, Insn> {
        self.instructions.iter()
    }

    pub fn first_address_after_code(&self) -> u64 {
        self.first_address_after_code
    }

    pub fn contains_address(&self, address: u64) -> bool {
        self.instructions.contains_key(&address)
    }

    pub fn contains(&self, instr: Insn) -> bool {
        self.instructions.contains_key(&instr.address)
    }

    pub fn in_bounds(&self, address: u64) -> bool {
        address >= self.first_address && address < self.first_address_after_code
    }

    pub fn get_next(&self, address: u64) -> u64 {
        match (address + 1..self.first_address_after_code - 1)
            .find(|a| self.instructions.contains_key(a))
        {
            Some(address) => address,
            None => panic!("Failed to find the next address."),
        }
    }

    pub fn get_in_value(&self, address: u64) -> u32 {
        self.reverse_links[&address].len() as u32
    }

    pub fn get_out_value(&self, address: u64) -> u32 {
        self.instruction_links[&address].len() as u32
    }

    pub fn get_bytes(&self, address: u64, size: usize) -> &[u8] {
        let addr = self.to_byte_array_index(address);
        &self.bytes[addr..(addr + size)]
    }

    pub fn get_extra_data(&mut self, address: u64) -> &mut ExtraData {
        if !self.extra_data.contains_key(&address) {
            let data = ExtraData {
                function_address: 0,
                import_name: "".to_string(),
                is_protected: false,
            };
            self.extra_data.insert(address, data);
        }
        self.extra_data.get_mut(&address).unwrap()
    }

    pub fn add_link(&mut self, offset: u64, target_offset: u64, type_: LinkType) -> () {
        let link = Link {
            address: offset,
            target_address: target_offset,
            type_,
        };

        let links = match self.instruction_links.get_mut(&offset) {
            Some(links) => links,
            None => {
                let links = Vec::new();
                self.instruction_links.insert(offset, links);
                self.instruction_links.get_mut(&offset).unwrap()
            }
        };
        links.push(link.clone());

        let reverse_links = match self.reverse_links.get_mut(&offset) {
            Some(links) => links,
            None => {
                let links = Vec::new();
                self.instruction_links.insert(offset, links);
                self.instruction_links.get_mut(&offset).unwrap()
            }
        };
        reverse_links.push(link);
    }

    pub fn add_jump_table_entry(&mut self, address: u64) -> bool {
        let data = self.read_u32(address);
        if !self.in_bounds(data as u64) {
            false
        } else {
            self.mark_data_bytes(address, 4, "TODO: TEXT".to_string());
            true
        }
    }

    pub fn add_jump_table_indirect_entries(&mut self, address: u64, count: u8) -> () {
        let display_text = "TODO: TEXT".to_string();
        self.mark_data_bytes(address, count, display_text);
    }

    pub fn mark_data_bytes(&mut self, address: u64, length: u8, data_display_text: String) -> () {
        // check if there is an instruction at address, otherwise split an existing one
        let mut size = match self.instructions.get(&address) {
            Some(instr) => instr.length,
            None => {
                let insn = self.split_instruction_at(address);
                insn.length - ((address - insn.address) as u8)
            }
        };
        // write a pseudo instruction
        // TODO: add hex string
        self.instructions.insert(
            address,
            Insn {
                address: address,
                code: ud_mnemonic_code::UD_Inone,
                length: length,
                hex: "<TODO:HEX>".to_string(),
                assembly: data_display_text,
                operands: vec![],
                prefix_rex: 0,
                prefix_segment: ud_type::UD_NONE,
                prefix_operand_size: false,
                prefix_address_size: false,
                prefix_lock: 0,
                prefix_str: 0,
                prefix_rep: 0,
                prefix_repe: 0,
                prefix_repne: 0,
            },
        );
        self.get_extra_data(address).is_protected = true;
        // remove old instructions
        while size < length {
            let insn = self
                .instructions
                .remove(&(address + (size as u64)))
                .unwrap();
            size += insn.length;
        }
        // if there are bytes left from the last removed instruction, re-disassemble them
        if size != length {
            self.disassembler.set_pc(address + (length as u64));
            let new_instructions = self.disassembler.disassemble(
                self.bytes,
                self.to_byte_array_index(address) + (length as usize),
                (size - length) as usize,
            );
            for new_insn in new_instructions {
                self.instructions.insert(new_insn.address, new_insn);
            }
        }
    }

    pub fn split_instruction_at(&mut self, address: u64) -> Insn {
        let mut insn_address = address - 1;
        while !self.instructions.contains_key(&insn_address) {
            insn_address -= 1;
        }
        let old_insn = self.instructions.get(&insn_address).unwrap().clone();
        // Split the old instruction and re-disassemble its first part
        let extra_length = (address - insn_address) as usize;
        self.disassembler.set_pc(insn_address);
        let new_instructions = self.disassembler.disassemble(
            self.bytes,
            self.to_byte_array_index(insn_address),
            extra_length,
        );
        for new_insn in new_instructions {
            self.instructions.insert(new_insn.address, new_insn);
        }
        old_insn
    }

    pub fn read_u32(&self, address: u64) -> u32 {
        let addr = (address - self.first_address) as usize;
        let mut result = self.bytes[addr] as u32;
        result |= (self.bytes[addr + 1] as u32) << 8;
        result |= (self.bytes[addr + 2] as u32) << 16;
        result |= (self.bytes[addr + 3] as u32) << 24;
        result
    }

    pub fn redisassemble(&mut self, address: u64) -> () {
        // This function will fix any instructions that were incorrectly disassembled because of the data block that was treated as code
        if !self.instructions.contains_key(&address) {
            self.split_instruction_at(address);
        }
        self.disassembler.set_pc(address);
        let max_disasm_length = 0x100;
        let mut disasm_length =
            std::cmp::min(max_disasm_length, self.first_address_after_code - address);
        for j in 0..disasm_length {
            match self.extra_data.get(&(address + j)) {
                Some(data) if data.is_protected => {
                    disasm_length = j;
                }
                _ => (),
            }
        }
        let mut new_instructions = self.disassembler.disassemble(
            self.bytes,
            self.to_byte_array_index(address),
            disasm_length as usize,
        );
        // Take 1 instruction less since it can be incorrectly disassembled (partial data)
        new_instructions.pop();
        let mut fixed_instructions = false;
        for new_insn in new_instructions {
            match self.instructions.get(&new_insn.address) {
                Some(old_insn) if old_insn.length == new_insn.length => {
                    if fixed_instructions {
                        return;
                    }
                }
                _ => {
                    let length = new_insn.length as u64;
                    let addr = new_insn.address;
                    self.instructions.insert(new_insn.address, new_insn);
                    for j in 1..(length - 1) {
                        self.instructions.remove(&(addr + j));
                    }
                    fixed_instructions = true;
                }
            }
        }
        panic!("FIXME: Extra disassemble size was too small");
    }

    fn to_byte_array_index(&self, address: u64) -> usize {
        (address - self.first_address) as usize
    }
}
<|endoftext|>


<commit_before>//! Code sink that writes binary machine code into contiguous memory.
//!
//! The `CodeSink` trait is the most general way of extracting binary machine code from Cretonne,
//! and it is implemented by things like the `test binemit` file test driver to generate
//! hexadecimal machine code. The `CodeSink` has some undesirable performance properties because of
//! the dual abstraction: `TargetIsa` is a trait object implemented by each supported ISA, so it
//! can't have any generic functions that could be specialized for each `CodeSink` implementation.
//! This results in many virtual function callbacks (one per `put*` call) when
//! `TargetIsa::emit_inst()` is used.
//!
//! The `MemoryCodeSink` type fixes the performance problem because it is a type known to
//! `TargetIsa` so it can specialize its machine code generation for the type. The trade-off is
//! that a `MemoryCodeSink` will always write binary machine code to raw memory. It forwards any
//! relocations to a `RelocSink` trait object. Relocations are less frequent than the
//! `CodeSink::put*` methods, so the performance impact of the virtual callbacks is less severe.

use super::{Addend, CodeOffset, CodeSink, Reloc};
use ir::{ExternalName, JumpTable, SourceLoc, TrapCode};
use std::ptr::write_unaligned;

/// A `CodeSink` that writes binary machine code directly into memory.
///
/// A `MemoryCodeSink` object should be used when emitting a Cretonne IR function into executable
/// memory. It writes machine code directly to a raw pointer without any bounds checking, so make
/// sure to allocate enough memory for the whole function. The number of bytes required is returned
/// by the `Context::compile()` function.
///
/// Any relocations in the function are forwarded to the `RelocSink` trait object.
///
/// Note that `MemoryCodeSink` writes multi-byte values in the native byte order of the host. This
/// is not the right thing to do for cross compilation.
pub struct MemoryCodeSink<'a> {
    data: *mut u8,
    offset: isize,
    relocs: &'a mut RelocSink,
    traps: &'a mut TrapSink,
}

impl<'a> MemoryCodeSink<'a> {
    /// Create a new memory code sink that writes a function to the memory pointed to by `data`.
    ///
    /// This function is unsafe since `MemoryCodeSink` does not perform bounds checking on the
    /// memory buffer, and it can't guarantee that the `data` pointer is valid.
    pub unsafe fn new<'sink>(
        data: *mut u8,
        relocs: &'sink mut RelocSink,
        traps: &'sink mut TrapSink,
    ) -> MemoryCodeSink<'sink> {
        MemoryCodeSink {
            data,
            offset: 0,
            relocs,
            traps,
        }
    }
}

/// A trait for receiving relocations for code that is emitted directly into memory.
pub trait RelocSink {
    /// Add a relocation referencing an EBB at the current offset.
    fn reloc_ebb(&mut self, CodeOffset, Reloc, CodeOffset);

    /// Add a relocation referencing an external symbol at the current offset.
    fn reloc_external(&mut self, CodeOffset, Reloc, &ExternalName, Addend);

    /// Add a relocation referencing a jump table.
    fn reloc_jt(&mut self, CodeOffset, Reloc, JumpTable);
}

/// A trait for receiving trap codes and offsets.
pub trait TrapSink {
    /// Add trap information for a specific offset.
    fn trap(&mut self, CodeOffset, SourceLoc, TrapCode);
}

impl<'a> CodeSink for MemoryCodeSink<'a> {
    fn offset(&self) -> CodeOffset {
        self.offset as CodeOffset
    }

    fn put1(&mut self, x: u8) {
        unsafe {
            write_unaligned(self.data.offset(self.offset), x);
        }
        self.offset += 1;
    }

    fn put2(&mut self, x: u16) {
        unsafe {
            #[cfg_attr(feature = "cargo-clippy", allow(cast_ptr_alignment))]
            write_unaligned(self.data.offset(self.offset) as *mut u16, x);
        }
        self.offset += 2;
    }

    fn put4(&mut self, x: u32) {
        unsafe {
            #[cfg_attr(feature = "cargo-clippy", allow(cast_ptr_alignment))]
            write_unaligned(self.data.offset(self.offset) as *mut u32, x);
        }
        self.offset += 4;
    }

    fn put8(&mut self, x: u64) {
        unsafe {
            #[cfg_attr(feature = "cargo-clippy", allow(cast_ptr_alignment))]
            write_unaligned(self.data.offset(self.offset) as *mut u64, x);
        }
        self.offset += 8;
    }

    fn reloc_ebb(&mut self, rel: Reloc, ebb_offset: CodeOffset) {
        let ofs = self.offset();
        self.relocs.reloc_ebb(ofs, rel, ebb_offset);
    }

    fn reloc_external(&mut self, rel: Reloc, name: &ExternalName, addend: Addend) {
        let ofs = self.offset();
        self.relocs.reloc_external(ofs, rel, name, addend);
    }

    fn reloc_jt(&mut self, rel: Reloc, jt: JumpTable) {
        let ofs = self.offset();
        self.relocs.reloc_jt(ofs, rel, jt);
    }

    fn trap(&mut self, code: TrapCode, srcloc: SourceLoc) {
        let ofs = self.offset();
        self.traps.trap(ofs, srcloc, code);
    }
}

/// A `TrapSink` implementation that does nothing, which is convenient when
/// compiling code that does not rely on trapping semantics.
pub struct NullTrapSink {}

impl TrapSink for NullTrapSink {
    fn trap(&mut self, _offset: CodeOffset, _srcloc: SourceLoc, _code: TrapCode) {}
}
<commit_msg>Add a comment advertising `NullTrapSink`.<commit_after>//! Code sink that writes binary machine code into contiguous memory.
//!
//! The `CodeSink` trait is the most general way of extracting binary machine code from Cretonne,
//! and it is implemented by things like the `test binemit` file test driver to generate
//! hexadecimal machine code. The `CodeSink` has some undesirable performance properties because of
//! the dual abstraction: `TargetIsa` is a trait object implemented by each supported ISA, so it
//! can't have any generic functions that could be specialized for each `CodeSink` implementation.
//! This results in many virtual function callbacks (one per `put*` call) when
//! `TargetIsa::emit_inst()` is used.
//!
//! The `MemoryCodeSink` type fixes the performance problem because it is a type known to
//! `TargetIsa` so it can specialize its machine code generation for the type. The trade-off is
//! that a `MemoryCodeSink` will always write binary machine code to raw memory. It forwards any
//! relocations to a `RelocSink` trait object. Relocations are less frequent than the
//! `CodeSink::put*` methods, so the performance impact of the virtual callbacks is less severe.

use super::{Addend, CodeOffset, CodeSink, Reloc};
use ir::{ExternalName, JumpTable, SourceLoc, TrapCode};
use std::ptr::write_unaligned;

/// A `CodeSink` that writes binary machine code directly into memory.
///
/// A `MemoryCodeSink` object should be used when emitting a Cretonne IR function into executable
/// memory. It writes machine code directly to a raw pointer without any bounds checking, so make
/// sure to allocate enough memory for the whole function. The number of bytes required is returned
/// by the `Context::compile()` function.
///
/// Any relocations in the function are forwarded to the `RelocSink` trait object.
///
/// Note that `MemoryCodeSink` writes multi-byte values in the native byte order of the host. This
/// is not the right thing to do for cross compilation.
pub struct MemoryCodeSink<'a> {
    data: *mut u8,
    offset: isize,
    relocs: &'a mut RelocSink,
    traps: &'a mut TrapSink,
}

impl<'a> MemoryCodeSink<'a> {
    /// Create a new memory code sink that writes a function to the memory pointed to by `data`.
    ///
    /// This function is unsafe since `MemoryCodeSink` does not perform bounds checking on the
    /// memory buffer, and it can't guarantee that the `data` pointer is valid.
    pub unsafe fn new<'sink>(
        data: *mut u8,
        relocs: &'sink mut RelocSink,
        traps: &'sink mut TrapSink,
    ) -> MemoryCodeSink<'sink> {
        MemoryCodeSink {
            data,
            offset: 0,
            relocs,
            traps,
        }
    }
}

/// A trait for receiving relocations for code that is emitted directly into memory.
pub trait RelocSink {
    /// Add a relocation referencing an EBB at the current offset.
    fn reloc_ebb(&mut self, CodeOffset, Reloc, CodeOffset);

    /// Add a relocation referencing an external symbol at the current offset.
    fn reloc_external(&mut self, CodeOffset, Reloc, &ExternalName, Addend);

    /// Add a relocation referencing a jump table.
    fn reloc_jt(&mut self, CodeOffset, Reloc, JumpTable);
}

/// A trait for receiving trap codes and offsets.
///
/// If you don't need information about possible traps, you can use the
/// [`NullTrapSink`](binemit/trait.TrapSink.html) implementation.
pub trait TrapSink {
    /// Add trap information for a specific offset.
    fn trap(&mut self, CodeOffset, SourceLoc, TrapCode);
}

impl<'a> CodeSink for MemoryCodeSink<'a> {
    fn offset(&self) -> CodeOffset {
        self.offset as CodeOffset
    }

    fn put1(&mut self, x: u8) {
        unsafe {
            write_unaligned(self.data.offset(self.offset), x);
        }
        self.offset += 1;
    }

    fn put2(&mut self, x: u16) {
        unsafe {
            #[cfg_attr(feature = "cargo-clippy", allow(cast_ptr_alignment))]
            write_unaligned(self.data.offset(self.offset) as *mut u16, x);
        }
        self.offset += 2;
    }

    fn put4(&mut self, x: u32) {
        unsafe {
            #[cfg_attr(feature = "cargo-clippy", allow(cast_ptr_alignment))]
            write_unaligned(self.data.offset(self.offset) as *mut u32, x);
        }
        self.offset += 4;
    }

    fn put8(&mut self, x: u64) {
        unsafe {
            #[cfg_attr(feature = "cargo-clippy", allow(cast_ptr_alignment))]
            write_unaligned(self.data.offset(self.offset) as *mut u64, x);
        }
        self.offset += 8;
    }

    fn reloc_ebb(&mut self, rel: Reloc, ebb_offset: CodeOffset) {
        let ofs = self.offset();
        self.relocs.reloc_ebb(ofs, rel, ebb_offset);
    }

    fn reloc_external(&mut self, rel: Reloc, name: &ExternalName, addend: Addend) {
        let ofs = self.offset();
        self.relocs.reloc_external(ofs, rel, name, addend);
    }

    fn reloc_jt(&mut self, rel: Reloc, jt: JumpTable) {
        let ofs = self.offset();
        self.relocs.reloc_jt(ofs, rel, jt);
    }

    fn trap(&mut self, code: TrapCode, srcloc: SourceLoc) {
        let ofs = self.offset();
        self.traps.trap(ofs, srcloc, code);
    }
}

/// A `TrapSink` implementation that does nothing, which is convenient when
/// compiling code that does not rely on trapping semantics.
pub struct NullTrapSink {}

impl TrapSink for NullTrapSink {
    fn trap(&mut self, _offset: CodeOffset, _srcloc: SourceLoc, _code: TrapCode) {}
}
<|endoftext|>


<commit_before>extern crate openssl;
use std::str;
use openssl::hash::{hash2, MessageDigest};

fn main() {
    let data: &[u8] = b"Hello, world";
    let digest = hash2(MessageDigest::sha256(), &data);

    println!("{}", str::from_utf8(data).ok().unwrap());
    println!("hash: {:?}", digest);
}
<commit_msg>fix ssl hash rename<commit_after>extern crate openssl;
use std::str;
use openssl::hash::{hash, MessageDigest};

fn main() {
    let data: &[u8] = b"Hello, world";
    let digest = hash(MessageDigest::sha256(), &data);

    println!("{}", str::from_utf8(data).ok().unwrap());
    println!("hash: {:?}", digest);
}
<|endoftext|>


<commit_before>#![allow(unused_must_use)]

extern crate syntax;
extern crate rustc;
extern crate serialize;
extern crate getopts;

use getopts::{optflag,getopts};

use std::io;
use std::os;
use std::io::Reader;
use std::vec::FromVec;
use std::string::String;
use serialize::json;
use serialize::json::{Json, List, String, Object};

use rustc::driver::{driver, session, config};

fn filter_json(j: &mut json::Json) {
    let mut replace = None;

    match *j {
        json::Object(ref mut ob) => {
            // remove
            ob.pop(&String::from_str("lifetimes"));
            ob.pop(&String::from_str("global"));
            ob.pop(&String::from_str("types"));
            ob.pop(&String::from_str("span"));
            ob.pop(&String::from_str("id"));
            let mut kv : Option<(String,~[Json])> = None;
            match ob.find(&String::from_str("node")) {
                Some(&json::Object(ref ob2)) => {
                    match (ob2.find(&String::from_str("variant")),
                           ob2.find(&String::from_str("fields"))) {
                        (Some(&String(ref s)), Some(&List(ref ls))) => {
                            kv = Some((s.clone(), FromVec::from_vec(ls.clone())));
                        }
                        _ => ()
                    }
                }
                _ => ()
            }
            match kv {
                None => (),
                Some((k, v)) => {
                    ob.pop(&String::from_str("node"));
                    ob.insert(k, List(Vec::from_slice(v)));
                }
            }
            for (_, v) in ob.mut_iter() {
                filter_json(v);
            }
        }
        json::List(ref mut ls) => {
            for v in ls.mut_iter() {
                filter_json(v)
            }
            if ls.len() == 1 {
                replace = Some(ls.as_slice()[0].clone())
            }
        }
        _ => ()
    }
    match replace {
        None => (),
        Some(jj) => *j = jj
    }
}

static indent_step: int = 4;

fn print_indent(indent: int) {
    let mut out = io::stdout();
    for i in range(0, indent) {
        if i % indent_step == 0 {
            out.write_str("|");
        } else {
            out.write_str(" ");
        }
    }
}

fn print_sexp(indent: int, j: &json::Json) {
    let mut out = io::stdout();

    match *j {

        json::Object(ref ob) => {
            {
                for (k, v) in ob.iter() {
                    print_indent(indent);
                    out.write_str("(");
                    out.write_str(k.as_slice());
                    out.write_str("\n");
                    print_sexp(indent + indent_step, v);
                    print_indent(indent);
                    out.write_str(")\n");
                }
            }
        }
        json::List(ref ls) => {
            print_indent(indent);
            out.write_str("(\n");
            for v in ls.iter() {
                print_sexp(indent + indent_step, v);
            }
            print_indent(indent);
            out.write_str(")\n");
        }
        json::String(ref s) => {
            print_indent(indent);
            out.write_str(s.as_slice());
            out.write_str("\n");
        }
        json::Null => {
            print_indent(indent);
            out.write_str("<nil>\n");
        }
        json::Boolean(true) => {
            print_indent(indent);
            out.write_str("true\n");
        }
        json::Boolean(false) => {
            print_indent(indent);
            out.write_str("false\n");
        }
        json::Number(n) => {
            print_indent(indent);
            out.write_str(n.to_str().as_slice());
            out.write_str("\n");
        }
    }
}

fn main() {

    let args = os::args().move_iter().collect::<Vec<String>>();
    let opts = [ optflag("j", "", "dump output in JSON, not sexp") ];
    let matches = match getopts(args.tail(), opts) {
        Ok(m) => { m }
        Err(f) => { fail!(f.to_err_msg()) }
    };
    let dump_json = matches.opt_present("j");

    match io::stdin().read_to_str() {
        Ok(text) => {
            let opt = config::basic_options();
            let sess = session::build_session(opt, None);
            let cfg = config::build_configuration(&sess);
            let input = driver::StrInput(text);
            let cr = driver::phase_1_parse_input(&sess, cfg, &input);

            // JSON-ify, meaning "encode then re-parse as just json", ugh.
            let ast_str = json::Encoder::str_encode(&cr.module);
            let mut s = ast_str.to_str();
            let chars = Vec::from_fn(s.len(), |_| s.pop_char().unwrap());
            let mut b = json::Builder::new(chars.move_iter());
            let mut j = b.build().ok().unwrap();

            filter_json(&mut j);
            if dump_json {
                let mut writer = io::stdout();
                j.to_pretty_writer(&mut writer as &mut io::Writer);
            } else {
                print_sexp(0, &j);
            }
        },
        Err(ioerr) => {
            fail!("I/O Error: {}", ioerr.desc);
        }
    }
}
<commit_msg>Fix rparse.<commit_after>#![allow(unused_must_use)]

extern crate syntax;
extern crate rustc;
extern crate serialize;
extern crate getopts;

use getopts::{optflag,getopts};

use std::io;
use std::os;
use std::io::Reader;
use std::vec::FromVec;
use std::string::String;
use serialize::json;
use serialize::json::{Json, List, String, Object};

use rustc::driver::{driver, session, config};

fn filter_json(j: &mut json::Json) {
    let mut replace = None;

    match *j {
        json::Object(ref mut ob) => {
            // remove
            ob.pop(&String::from_str("span"));
            ob.pop(&String::from_str("id"));
            let mut kv : Option<(String,~[Json])> = None;
            match ob.find(&String::from_str("node")) {
                Some(&json::Object(ref ob2)) => {
                    match (ob2.find(&String::from_str("variant")),
                           ob2.find(&String::from_str("fields"))) {
                        (Some(&String(ref s)), Some(&List(ref ls))) => {
                            kv = Some((s.clone(), FromVec::from_vec(ls.clone())));
                        }
                        _ => ()
                    }
                }
                _ => ()
            }
            match kv {
                None => (),
                Some((k, v)) => {
                    ob.pop(&String::from_str("node"));
                    ob.insert(k, List(Vec::from_slice(v)));
                }
            }
            for (_, v) in ob.mut_iter() {
                filter_json(v);
            }
        }
        json::List(ref mut ls) => {
            for v in ls.mut_iter() {
                filter_json(v)
            }
            if ls.len() == 1 {
                replace = Some(ls.as_slice()[0].clone())
            }
        }
        _ => ()
    }
    match replace {
        None => (),
        Some(jj) => *j = jj
    }
}

static indent_step: int = 4;

fn print_indent(indent: int) {
    let mut out = io::stdout();
    for i in range(0, indent) {
        if i % indent_step == 0 {
            out.write_str("|");
        } else {
            out.write_str(" ");
        }
    }
}

fn print_sexp(indent: int, j: &json::Json) {
    let mut out = io::stdout();

    match *j {

        json::Object(ref ob) => {
            {
                for (k, v) in ob.iter() {
                    print_indent(indent);
                    out.write_str("(");
                    out.write_str(k.as_slice());
                    out.write_str("\n");
                    print_sexp(indent + indent_step, v);
                    print_indent(indent);
                    out.write_str(")\n");
                }
            }
        }
        json::List(ref ls) => {
            print_indent(indent);
            out.write_str("(\n");
            for v in ls.iter() {
                print_sexp(indent + indent_step, v);
            }
            print_indent(indent);
            out.write_str(")\n");
        }
        json::String(ref s) => {
            print_indent(indent);
            out.write_str(s.as_slice());
            out.write_str("\n");
        }
        json::Null => {
            print_indent(indent);
            out.write_str("<nil>\n");
        }
        json::Boolean(true) => {
            print_indent(indent);
            out.write_str("true\n");
        }
        json::Boolean(false) => {
            print_indent(indent);
            out.write_str("false\n");
        }
        json::Number(n) => {
            print_indent(indent);
            out.write_str(n.to_str().as_slice());
            out.write_str("\n");
        }
    }
}

fn main() {

    let args = os::args().move_iter().collect::<Vec<String>>();
    let opts = [ optflag("j", "", "dump output in JSON, not sexp") ];
    let matches = match getopts(args.tail(), opts) {
        Ok(m) => { m }
        Err(f) => { fail!(f.to_err_msg()) }
    };
    let dump_json = matches.opt_present("j");

    match io::stdin().read_to_str() {
        Ok(text) => {
            let opt = config::basic_options();
            let sess = session::build_session(opt, None);
            let cfg = config::build_configuration(&sess);
            let input = driver::StrInput(text);
            let cr = driver::phase_1_parse_input(&sess, cfg, &input);

            // JSON-ify, meaning "encode then re-parse as just json", ugh.
            let ast_str = json::Encoder::str_encode(&cr.module);
            let chars = ast_str.as_slice().chars().collect::<Vec<char>>();
            let mut b = json::Builder::new(chars.move_iter());
            let mut j = b.build().ok().unwrap();

            filter_json(&mut j);
            if dump_json {
                let mut writer = io::stdout();
                j.to_pretty_writer(&mut writer as &mut io::Writer);
            } else {
                print_sexp(0, &j);
            }
        },
        Err(ioerr) => {
            fail!("I/O Error: {}", ioerr.desc);
        }
    }
}
<|endoftext|>


<commit_before>extern crate eom;
extern crate ndarray;
extern crate ndarray_linalg;
extern crate num_traits;

use std::fs::*;
use std::io::Write;
use ndarray::*;
use ndarray_linalg::*;
use num_traits::int::PrimInt;

use eom::*;
use eom::traits::*;

fn check_accuracy<A, D, F, Sc>(mut teo: Sc, init: Array<A, D>, fname: &str)
where
    A: Scalar<Real = f64>,
    D: Dimension,
    F: Explicit<Scalar = A, Dim = D>,
    Sc: Scheme<F, Scalar = A, Dim = D, Time = f64>,
{
    let data: Vec<_> = (0..12)
        .map(|n| {
            let dt = 0.01 / 2.pow(n) as f64;
            let t = 1000 * 2.pow(n);
            teo.set_dt(dt);
            let mut ts = adaptor::time_series(init.clone(), &mut teo);
            (dt, ts.nth(t - 1).unwrap())
        })
        .collect();

    let mut f = File::create(fname).unwrap();
    write!(&mut f, "dt,dev\n").unwrap();
    for i in 0..(data.len() - 1) {
        let dt = data[i].0;
        let dev = (&data[i + 1].1 - &data[i].1).norm();
        write!(&mut f, "{:.08e},{:.08e}\n", dt, dev).unwrap();
    }
}

fn main() {
    let l63 = ode::Lorenz63::default();
    check_accuracy(
        explicit::Euler::new(l63.clone(), 1.0),
        arr1(&[1.0, 0.0, 0.0]),
        "euler.csv",
    );
    check_accuracy(
        explicit::Heun::new(l63.clone(), 1.0),
        arr1(&[1.0, 0.0, 0.0]),
        "heun.csv",
    );
    check_accuracy(
        explicit::RK4::new(l63.clone(), 1.0),
        arr1(&[1.0, 0.0, 0.0]),
        "rk4.csv",
    );
}
<commit_msg>Restore accuracy check of diag_rk4<commit_after>extern crate eom;
extern crate ndarray;
extern crate ndarray_linalg;
extern crate num_traits;

use std::fs::*;
use std::io::Write;
use ndarray::*;
use ndarray_linalg::*;
use num_traits::int::PrimInt;

use eom::*;
use eom::traits::*;

fn accuracy<A, D, F, Sc>(mut teo: Sc, init: Array<A, D>) -> Vec<(Sc::Time, A::Real)>
where
    A: Scalar,
    D: Dimension,
    Sc: Scheme<F, Scalar = A, Dim = D>,
{
    let data: Vec<_> = (0..12)
        .map(|n| {
            let dt = into_scalar(0.01 / 2.pow(n) as f64);
            let t = 1000 * 2.pow(n);
            teo.set_dt(dt);
            let mut ts = adaptor::time_series(init.clone(), &mut teo);
            (dt, ts.nth(t - 1).unwrap())
        })
        .collect();
    data.windows(2)
        .map(|w| {
            let dt = w[0].0;
            let dev = (&w[1].1 - &w[0].1).norm();
            (dt, dev)
        })
        .collect()
}

fn check_accuracy<A, D, F, Sc>(teo: Sc, init: Array<A, D>, fname: &str)
where
    A: Scalar<Real = f64>,
    D: Dimension,
    Sc: Scheme<F, Scalar = A, Dim = D, Time = f64>,
{
    let acc = accuracy(teo, init);
    let mut f = File::create(fname).unwrap();
    write!(&mut f, "dt,dev\n").unwrap();
    for &(dt, dev) in acc.iter() {
        write!(&mut f, "{:.08e},{:.08e}\n", dt, dev).unwrap();
    }
}

fn main() {
    let l63 = ode::Lorenz63::default();
    check_accuracy(
        explicit::Euler::new(l63.clone(), 1.0),
        arr1(&[1.0, 0.0, 0.0]),
        "euler.csv",
    );
    check_accuracy(
        explicit::Heun::new(l63.clone(), 1.0),
        arr1(&[1.0, 0.0, 0.0]),
        "heun.csv",
    );
    check_accuracy(
        explicit::RK4::new(l63.clone(), 1.0),
        arr1(&[1.0, 0.0, 0.0]),
        "rk4.csv",
    );
    check_accuracy(
        semi_implicit::DiagRK4::new(l63.clone(), 1.0),
        arr1(&[1.0, 0.0, 0.0]),
        "diag_rk4.csv",
    );
}
<|endoftext|>


<commit_before>use gdk_pixbuf::{PixbufLoader, PixbufLoaderExt};
use gio;
use markup5ever::{LocalName, Namespace, QualName};
use std::cell::RefCell;
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::rc::Rc;

use crate::allowed_url::{AllowedUrl, AllowedUrlError, Fragment};
use crate::create_node::create_node;
use crate::css::{cascade, Origin, Stylesheet};
use crate::error::LoadingError;
use crate::handle::LoadOptions;
use crate::io::{self, BinaryData};
use crate::node::{NodeData, NodeType, RsvgNode};
use crate::property_bag::PropertyBag;
use crate::structure::{IntrinsicDimensions, Svg};
use crate::surface_utils::shared_surface::SharedImageSurface;
use crate::text::NodeChars;
use crate::xml::xml_load_from_possibly_compressed_stream;

/// A loaded SVG file and its derived data
///
/// This contains the tree of nodes (SVG elements), the mapping
/// of id to node, and the CSS styles defined for this SVG.
pub struct Document {
    tree: RsvgNode,

    ids: HashMap<String, RsvgNode>,

    // These require interior mutability because we load the extern
    // resources all over the place.  Eventually we'll be able to do this
    // once, at loading time, and keep this immutable.
    externs: RefCell<Resources>,
    images: RefCell<Images>,

    // Once we do not need to load externs, we can drop this as well
    load_options: LoadOptions,
}

impl Document {
    pub fn load_from_stream(
        load_options: &LoadOptions,
        stream: &gio::InputStream,
        cancellable: Option<&gio::Cancellable>,
    ) -> Result<Document, LoadingError> {
        xml_load_from_possibly_compressed_stream(
            DocumentBuilder::new(load_options),
            load_options.unlimited_size,
            stream,
            cancellable,
        )
    }

    pub fn root(&self) -> RsvgNode {
        self.tree.clone()
    }

    pub fn lookup(&self, fragment: &Fragment) -> Result<RsvgNode, LoadingError> {
        if fragment.uri().is_some() {
            self.externs
                .borrow_mut()
                .lookup(&self.load_options, fragment)
        } else {
            self.lookup_node_by_id(fragment.fragment())
                .ok_or(LoadingError::BadUrl)
        }
    }

    pub fn lookup_node_by_id(&self, id: &str) -> Option<RsvgNode> {
        self.ids.get(id).map(|n| (*n).clone())
    }

    pub fn lookup_image(&self, href: &str) -> Result<SharedImageSurface, LoadingError> {
        self.images.borrow_mut().lookup(&self.load_options, href)
    }

    pub fn get_intrinsic_dimensions(&self) -> IntrinsicDimensions {
        let root = self.root();
        let node_data = root.borrow();

        assert!(node_data.get_type() == NodeType::Svg);
        node_data.get_impl::<Svg>().get_intrinsic_dimensions()
    }
}

struct Resources {
    resources: HashMap<AllowedUrl, Result<Rc<Document>, LoadingError>>,
}

impl Resources {
    pub fn new() -> Resources {
        Resources {
            resources: Default::default(),
        }
    }

    pub fn lookup(
        &mut self,
        load_options: &LoadOptions,
        fragment: &Fragment,
    ) -> Result<RsvgNode, LoadingError> {
        if let Some(ref href) = fragment.uri() {
            self.get_extern_document(load_options, href)
                .and_then(|doc| {
                    doc.lookup_node_by_id(fragment.fragment())
                        .ok_or(LoadingError::BadUrl)
                })
        } else {
            unreachable!();
        }
    }

    fn get_extern_document(
        &mut self,
        load_options: &LoadOptions,
        href: &str,
    ) -> Result<Rc<Document>, LoadingError> {
        let aurl = AllowedUrl::from_href(href, load_options.base_url.as_ref())
            .map_err(|_| LoadingError::BadUrl)?;

        match self.resources.entry(aurl) {
            Entry::Occupied(e) => e.get().clone(),
            Entry::Vacant(e) => {
                let aurl = e.key();
                // FIXME: pass a cancellable to these
                let doc = io::acquire_stream(aurl, None)
                    .and_then(|stream| {
                        Document::load_from_stream(
                            &load_options.copy_with_base_url(aurl),
                            &stream,
                            None,
                        )
                    })
                    .map(Rc::new);
                let res = e.insert(doc);
                res.clone()
            }
        }
    }
}

struct Images {
    images: HashMap<AllowedUrl, Result<SharedImageSurface, LoadingError>>,
}

impl Images {
    pub fn new() -> Images {
        Images {
            images: Default::default(),
        }
    }

    pub fn lookup(
        &mut self,
        load_options: &LoadOptions,
        href: &str,
    ) -> Result<SharedImageSurface, LoadingError> {
        let aurl = AllowedUrl::from_href(href, load_options.base_url.as_ref())
            .map_err(|_| LoadingError::BadUrl)?;

        match self.images.entry(aurl) {
            Entry::Occupied(e) => e.get().clone(),
            Entry::Vacant(e) => {
                let surface = load_image(load_options, e.key());
                let res = e.insert(surface);
                res.clone()
            }
        }
    }
}

fn load_image(
    load_options: &LoadOptions,
    aurl: &AllowedUrl,
) -> Result<SharedImageSurface, LoadingError> {
    let BinaryData {
        data: bytes,
        content_type,
    } = io::acquire_data(&aurl, None)?;

    if bytes.is_empty() {
        return Err(LoadingError::EmptyData);
    }

    let loader = if let Some(ref content_type) = content_type {
        PixbufLoader::new_with_mime_type(content_type)?
    } else {
        PixbufLoader::new()
    };

    loader.write(&bytes)?;
    loader.close()?;

    let pixbuf = loader.get_pixbuf().ok_or(LoadingError::Unknown)?;

    let bytes = if load_options.keep_image_data {
        Some(bytes)
    } else {
        None
    };

    let surface =
        SharedImageSurface::from_pixbuf(&pixbuf, bytes, content_type.as_ref().map(String::as_str))?;

    Ok(surface)
}

pub struct DocumentBuilder {
    load_options: LoadOptions,
    tree: Option<RsvgNode>,
    ids: HashMap<String, RsvgNode>,
    stylesheets: Vec<Stylesheet>,
}

impl DocumentBuilder {
    pub fn new(load_options: &LoadOptions) -> DocumentBuilder {
        DocumentBuilder {
            load_options: load_options.clone(),
            tree: None,
            ids: HashMap::new(),
            stylesheets: Vec::new(),
        }
    }

    pub fn append_stylesheet_from_xml_processing_instruction(
        &mut self,
        alternate: Option<String>,
        type_: Option<String>,
        href: &str,
    ) -> Result<(), LoadingError> {
        if type_.as_ref().map(String::as_str) != Some("text/css")
            || (alternate.is_some() && alternate.as_ref().map(String::as_str) != Some("no"))
        {
            return Err(LoadingError::BadStylesheet);
        }

        // FIXME: handle CSS errors
        if let Ok(stylesheet) =
            Stylesheet::from_href(href, self.load_options.base_url.as_ref(), Origin::Author)
        {
            self.stylesheets.push(stylesheet);
        }

        Ok(())
    }

    pub fn append_element(
        &mut self,
        name: &QualName,
        pbag: &PropertyBag,
        parent: Option<RsvgNode>,
    ) -> RsvgNode {
        let mut node = create_node(name, pbag);

        if let Some(id) = node.borrow().get_id() {
            // This is so we don't overwrite an existing id
            self.ids
                .entry(id.to_string())
                .or_insert_with(|| node.clone());
        }

        node.borrow_mut()
            .set_atts(parent.as_ref().clone(), pbag, self.load_options.locale());

        if let Some(mut parent) = parent {
            parent.append(node.clone());
        } else if self.tree.is_none() {
            self.tree = Some(node.clone());
        } else {
            panic!("The tree root has already been set");
        }

        node
    }

    pub fn append_stylesheet_from_text(&mut self, text: &str) {
        // FIXME: handle CSS errors
        if let Ok(stylesheet) =
            Stylesheet::from_data(text, self.load_options.base_url.as_ref(), Origin::Author)
        {
            self.stylesheets.push(stylesheet);
        }
    }

    pub fn append_characters(&mut self, text: &str, parent: &mut RsvgNode) {
        if !text.is_empty() {
            self.append_chars_to_parent(text, parent);
        }
    }

    fn append_chars_to_parent(&mut self, text: &str, parent: &mut RsvgNode) {
        // When the last child is a Chars node we can coalesce
        // the text and avoid screwing up the Pango layouts
        let chars_node = if let Some(child) = parent
            .last_child()
            .filter(|c| c.borrow().get_type() == NodeType::Chars)
        {
            child
        } else {
            let child = RsvgNode::new(NodeData::new(
                NodeType::Chars,
                &QualName::new(
                    None,
                    Namespace::from("https://wiki.gnome.org/Projects/LibRsvg"),
                    LocalName::from("rsvg-chars"),
                ),
                None,
                None,
                Box::new(NodeChars::new()),
            ));

            parent.append(child.clone());

            child
        };

        chars_node.borrow().get_impl::<NodeChars>().append(text);
    }

    pub fn resolve_href(&self, href: &str) -> Result<AllowedUrl, AllowedUrlError> {
        AllowedUrl::from_href(href, self.load_options.base_url.as_ref())
    }

    pub fn build(self) -> Result<Document, LoadingError> {
        let DocumentBuilder {
            load_options,
            tree,
            ids,
            stylesheets,
            ..
        } = self;

        match tree {
            None => Err(LoadingError::SvgHasNoElements),
            Some(mut root) => {
                if root.borrow().get_type() == NodeType::Svg {
                    cascade(&mut root, &stylesheets);

                    Ok(Document {
                        tree: root.clone(),
                        ids,
                        externs: RefCell::new(Resources::new()),
                        images: RefCell::new(Images::new()),
                        load_options: load_options.clone(),
                    })
                } else {
                    Err(LoadingError::RootElementIsNotSvg)
                }
            }
        }
    }
}
<commit_msg>Images: make methods private<commit_after>use gdk_pixbuf::{PixbufLoader, PixbufLoaderExt};
use gio;
use markup5ever::{LocalName, Namespace, QualName};
use std::cell::RefCell;
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::rc::Rc;

use crate::allowed_url::{AllowedUrl, AllowedUrlError, Fragment};
use crate::create_node::create_node;
use crate::css::{cascade, Origin, Stylesheet};
use crate::error::LoadingError;
use crate::handle::LoadOptions;
use crate::io::{self, BinaryData};
use crate::node::{NodeData, NodeType, RsvgNode};
use crate::property_bag::PropertyBag;
use crate::structure::{IntrinsicDimensions, Svg};
use crate::surface_utils::shared_surface::SharedImageSurface;
use crate::text::NodeChars;
use crate::xml::xml_load_from_possibly_compressed_stream;

/// A loaded SVG file and its derived data
///
/// This contains the tree of nodes (SVG elements), the mapping
/// of id to node, and the CSS styles defined for this SVG.
pub struct Document {
    tree: RsvgNode,

    ids: HashMap<String, RsvgNode>,

    // These require interior mutability because we load the extern
    // resources all over the place.  Eventually we'll be able to do this
    // once, at loading time, and keep this immutable.
    externs: RefCell<Resources>,
    images: RefCell<Images>,

    // Once we do not need to load externs, we can drop this as well
    load_options: LoadOptions,
}

impl Document {
    pub fn load_from_stream(
        load_options: &LoadOptions,
        stream: &gio::InputStream,
        cancellable: Option<&gio::Cancellable>,
    ) -> Result<Document, LoadingError> {
        xml_load_from_possibly_compressed_stream(
            DocumentBuilder::new(load_options),
            load_options.unlimited_size,
            stream,
            cancellable,
        )
    }

    pub fn root(&self) -> RsvgNode {
        self.tree.clone()
    }

    pub fn lookup(&self, fragment: &Fragment) -> Result<RsvgNode, LoadingError> {
        if fragment.uri().is_some() {
            self.externs
                .borrow_mut()
                .lookup(&self.load_options, fragment)
        } else {
            self.lookup_node_by_id(fragment.fragment())
                .ok_or(LoadingError::BadUrl)
        }
    }

    pub fn lookup_node_by_id(&self, id: &str) -> Option<RsvgNode> {
        self.ids.get(id).map(|n| (*n).clone())
    }

    pub fn lookup_image(&self, href: &str) -> Result<SharedImageSurface, LoadingError> {
        self.images.borrow_mut().lookup(&self.load_options, href)
    }

    pub fn get_intrinsic_dimensions(&self) -> IntrinsicDimensions {
        let root = self.root();
        let node_data = root.borrow();

        assert!(node_data.get_type() == NodeType::Svg);
        node_data.get_impl::<Svg>().get_intrinsic_dimensions()
    }
}

struct Resources {
    resources: HashMap<AllowedUrl, Result<Rc<Document>, LoadingError>>,
}

impl Resources {
    pub fn new() -> Resources {
        Resources {
            resources: Default::default(),
        }
    }

    pub fn lookup(
        &mut self,
        load_options: &LoadOptions,
        fragment: &Fragment,
    ) -> Result<RsvgNode, LoadingError> {
        if let Some(ref href) = fragment.uri() {
            self.get_extern_document(load_options, href)
                .and_then(|doc| {
                    doc.lookup_node_by_id(fragment.fragment())
                        .ok_or(LoadingError::BadUrl)
                })
        } else {
            unreachable!();
        }
    }

    fn get_extern_document(
        &mut self,
        load_options: &LoadOptions,
        href: &str,
    ) -> Result<Rc<Document>, LoadingError> {
        let aurl = AllowedUrl::from_href(href, load_options.base_url.as_ref())
            .map_err(|_| LoadingError::BadUrl)?;

        match self.resources.entry(aurl) {
            Entry::Occupied(e) => e.get().clone(),
            Entry::Vacant(e) => {
                let aurl = e.key();
                // FIXME: pass a cancellable to these
                let doc = io::acquire_stream(aurl, None)
                    .and_then(|stream| {
                        Document::load_from_stream(
                            &load_options.copy_with_base_url(aurl),
                            &stream,
                            None,
                        )
                    })
                    .map(Rc::new);
                let res = e.insert(doc);
                res.clone()
            }
        }
    }
}

struct Images {
    images: HashMap<AllowedUrl, Result<SharedImageSurface, LoadingError>>,
}

impl Images {
    fn new() -> Images {
        Images {
            images: Default::default(),
        }
    }

    fn lookup(
        &mut self,
        load_options: &LoadOptions,
        href: &str,
    ) -> Result<SharedImageSurface, LoadingError> {
        let aurl = AllowedUrl::from_href(href, load_options.base_url.as_ref())
            .map_err(|_| LoadingError::BadUrl)?;

        match self.images.entry(aurl) {
            Entry::Occupied(e) => e.get().clone(),
            Entry::Vacant(e) => {
                let surface = load_image(load_options, e.key());
                let res = e.insert(surface);
                res.clone()
            }
        }
    }
}

fn load_image(
    load_options: &LoadOptions,
    aurl: &AllowedUrl,
) -> Result<SharedImageSurface, LoadingError> {
    let BinaryData {
        data: bytes,
        content_type,
    } = io::acquire_data(&aurl, None)?;

    if bytes.is_empty() {
        return Err(LoadingError::EmptyData);
    }

    let loader = if let Some(ref content_type) = content_type {
        PixbufLoader::new_with_mime_type(content_type)?
    } else {
        PixbufLoader::new()
    };

    loader.write(&bytes)?;
    loader.close()?;

    let pixbuf = loader.get_pixbuf().ok_or(LoadingError::Unknown)?;

    let bytes = if load_options.keep_image_data {
        Some(bytes)
    } else {
        None
    };

    let surface =
        SharedImageSurface::from_pixbuf(&pixbuf, bytes, content_type.as_ref().map(String::as_str))?;

    Ok(surface)
}

pub struct DocumentBuilder {
    load_options: LoadOptions,
    tree: Option<RsvgNode>,
    ids: HashMap<String, RsvgNode>,
    stylesheets: Vec<Stylesheet>,
}

impl DocumentBuilder {
    pub fn new(load_options: &LoadOptions) -> DocumentBuilder {
        DocumentBuilder {
            load_options: load_options.clone(),
            tree: None,
            ids: HashMap::new(),
            stylesheets: Vec::new(),
        }
    }

    pub fn append_stylesheet_from_xml_processing_instruction(
        &mut self,
        alternate: Option<String>,
        type_: Option<String>,
        href: &str,
    ) -> Result<(), LoadingError> {
        if type_.as_ref().map(String::as_str) != Some("text/css")
            || (alternate.is_some() && alternate.as_ref().map(String::as_str) != Some("no"))
        {
            return Err(LoadingError::BadStylesheet);
        }

        // FIXME: handle CSS errors
        if let Ok(stylesheet) =
            Stylesheet::from_href(href, self.load_options.base_url.as_ref(), Origin::Author)
        {
            self.stylesheets.push(stylesheet);
        }

        Ok(())
    }

    pub fn append_element(
        &mut self,
        name: &QualName,
        pbag: &PropertyBag,
        parent: Option<RsvgNode>,
    ) -> RsvgNode {
        let mut node = create_node(name, pbag);

        if let Some(id) = node.borrow().get_id() {
            // This is so we don't overwrite an existing id
            self.ids
                .entry(id.to_string())
                .or_insert_with(|| node.clone());
        }

        node.borrow_mut()
            .set_atts(parent.as_ref().clone(), pbag, self.load_options.locale());

        if let Some(mut parent) = parent {
            parent.append(node.clone());
        } else if self.tree.is_none() {
            self.tree = Some(node.clone());
        } else {
            panic!("The tree root has already been set");
        }

        node
    }

    pub fn append_stylesheet_from_text(&mut self, text: &str) {
        // FIXME: handle CSS errors
        if let Ok(stylesheet) =
            Stylesheet::from_data(text, self.load_options.base_url.as_ref(), Origin::Author)
        {
            self.stylesheets.push(stylesheet);
        }
    }

    pub fn append_characters(&mut self, text: &str, parent: &mut RsvgNode) {
        if !text.is_empty() {
            self.append_chars_to_parent(text, parent);
        }
    }

    fn append_chars_to_parent(&mut self, text: &str, parent: &mut RsvgNode) {
        // When the last child is a Chars node we can coalesce
        // the text and avoid screwing up the Pango layouts
        let chars_node = if let Some(child) = parent
            .last_child()
            .filter(|c| c.borrow().get_type() == NodeType::Chars)
        {
            child
        } else {
            let child = RsvgNode::new(NodeData::new(
                NodeType::Chars,
                &QualName::new(
                    None,
                    Namespace::from("https://wiki.gnome.org/Projects/LibRsvg"),
                    LocalName::from("rsvg-chars"),
                ),
                None,
                None,
                Box::new(NodeChars::new()),
            ));

            parent.append(child.clone());

            child
        };

        chars_node.borrow().get_impl::<NodeChars>().append(text);
    }

    pub fn resolve_href(&self, href: &str) -> Result<AllowedUrl, AllowedUrlError> {
        AllowedUrl::from_href(href, self.load_options.base_url.as_ref())
    }

    pub fn build(self) -> Result<Document, LoadingError> {
        let DocumentBuilder {
            load_options,
            tree,
            ids,
            stylesheets,
            ..
        } = self;

        match tree {
            None => Err(LoadingError::SvgHasNoElements),
            Some(mut root) => {
                if root.borrow().get_type() == NodeType::Svg {
                    cascade(&mut root, &stylesheets);

                    Ok(Document {
                        tree: root.clone(),
                        ids,
                        externs: RefCell::new(Resources::new()),
                        images: RefCell::new(Images::new()),
                        load_options: load_options.clone(),
                    })
                } else {
                    Err(LoadingError::RootElementIsNotSvg)
                }
            }
        }
    }
}
<|endoftext|>