<commit_before>use crate::database::*;
use std::cmp::{max, min};
use std::collections::BTreeSet;
use std::fs::File;
use std::io::Write;
use std::iter::FromIterator;

// Get the shade colour for a tile type
fn get_colour(ttype: &str) -> &'static str {
    if ttype.contains("TAP") {
        return "#DDDDDD";
    } else if ttype.starts_with("SYSIO") {
        return "#88FF88";
    } else if ttype.starts_with("CIB") {
        return "#FF8888";
    } else if ttype.starts_with("PLC") {
        return "#8888FF";
    } else if ttype.starts_with("DUMMY") {
        return "#FFFFFF";
    } else if ttype.starts_with("MIB_EBR") || ttype.starts_with("EBR_") || ttype.starts_with("LRAM")
    {
        return "#FF88FF";
    } else if ttype.contains("DSP") || ttype.contains("ALU") {
        return "#FFFF88";
    } else if ttype.contains("PLL")
        || ttype.contains("DPHY")
        || ttype.contains("CDR")
        || ttype.contains("PCIE")
        || ttype.contains("EFB")
        || ttype.contains("PMU")
        || ttype.contains("ADC")
    {
        return "#88FFFF";
    } else {
        return "#888888";
    }
}

pub fn write_tilegrid_html(db: &mut Database, fam: &str, device: &str, filepath: &str) {
    let device_info = db.device_by_name(device).unwrap().2;
    let tilegrid = db.device_tilegrid(fam, device);
    let mut tiles: Vec<Vec<Vec<(&str, &str, &'static str)>>> =
        vec![vec![vec![]; (device_info.max_col + 1) as usize]; (device_info.max_row + 1) as usize];
    for (tileid, tiledata) in tilegrid.tiles.iter() {
        let name = tileid.split(':').next().unwrap();
        let colour = get_colour(&tiledata.tiletype);
        tiles[tiledata.y as usize][tiledata.x as usize].push((name, &tiledata.tiletype, colour));
    }
    let mut html = File::create(filepath).unwrap();
    write!(
        html,
        "\n\
<title>{d} Tiles</title>\n\
\n\

{d} Tilegrid
\n\

\n\
",
        d = device
    )
    .unwrap();
    for trow in tiles.iter() {
        writeln!(html, "
").unwrap();
        let row_max_height = trow.iter().map(Vec::len).fold(0, max);
        let row_height = max(75, 30 * row_max_height);
        for tloc in trow.iter() {
            writeln!(
                html,
                "",
                row_height
            )
            .unwrap();
            for (name, ttype, colour) in tloc.iter() {
                writeln!(html, "
{n}
{t}
",
                    h=(100.0 / (tloc.len() as f32)), c=colour, n=name, t=ttype).unwrap();
            }
            writeln!(html, "	").unwrap();
        }
        writeln!(html, "
").unwrap();
    }
}

// Get the colour given the first letter of  a wire label
fn wire_colour(c: char) -> &'static str {
    match c {
        'A' => "#88FFFF",
        'B' => "#FF88FF",
        'C' => "#8888FF",
        'D' => "#FFFF88",
        'M' => "#FFBBBB",
        'H' => "#BBBBFF",
        'V' => "#FFFFBB",
        _ => "#FF8888",
    }
}

pub fn write_bits_html(db: &mut Database, fam: &str, device: &str, tiletype: &str, filepath: &str) {
    let tilegrid = db.device_tilegrid(fam, device);
    let mut nframes = 0;
    let mut nbits = 0;
    for tile in tilegrid.tiles.values() {
        if tile.tiletype == tiletype {
            nframes = tile.frames;
            nbits = tile.bits;
            break;
        }
    }

    let bitdb = &db.tile_bitdb(fam, tiletype).db;

    // Find the purpose of each bit
    // (frame, bit) --> (link, labels)
    let mut bitgrid: Vec<Vec<(BTreeSet<String>, Option<String>)>> =
        vec![vec![(BTreeSet::new(), None); nbits]; nframes];
    for (wordname, data) in bitdb.words.iter() {
        for (i, bits) in data.bits.iter().enumerate() {
            for bit in bits {
                bitgrid[bit.frame][bit.bit]
                    .0
                    .insert(format!("{}[{}]", wordname, i));
                bitgrid[bit.frame][bit.bit].1 = Some(format!("word_{}", wordname));
            }
        }
    }
    for (enumname, data) in bitdb.enums.iter() {
        for bit in data.options.values().flatten() {
            bitgrid[bit.frame][bit.bit]
                .0
                .insert(format!("{}", enumname));
            bitgrid[bit.frame][bit.bit].1 = Some(format!("enum_{}", enumname));
        }
    }
    for (sink, data) in bitdb.pips.iter() {
        for bit in data.iter().map(|x| x.bits.iter()).flatten() {
            bitgrid[bit.frame][bit.bit].0.insert(format!("{}", sink));
            bitgrid[bit.frame][bit.bit].1 = Some(format!("mux_{}", sink));
        }
    }

    // Write out prelude HTML
    let mut html = File::create(filepath).unwrap();
    write!(
        html,
        " \n\
            <title>{} Bit Data</title>\n\
        ",
        tiletype
    )
    .unwrap();
    write!(
        html,
        "\n\
            \n\
            \n\
            "
    )
    .unwrap();

    // f, b -> (group, label, colour)
    let get_bit_info = |frame: usize, bit: usize| -> (String, String, &'static str) {
        match &bitgrid[frame][bit].1 {
            Some(group) => {
                let mut label = '?';
                let mut colour = "#FFFFFF";
                if group.starts_with("mux") {
                    let us_pos = group.rfind('_').unwrap();
                    label = group.chars().nth(us_pos + 1).unwrap();
                    if label == 'J' {
                        label = group.chars().nth(us_pos + 2).unwrap();
                    }
                    colour = wire_colour(label);
                } else if group.starts_with("enum") || group.starts_with("word") {
                    let us_pos = group.rfind(|c| c == '_' || c == '.').unwrap();
                    label = group.chars().nth(us_pos + 1).unwrap();
                    colour = "#88FF88";
                }
                (group.to_string(), label.to_string(), colour)
            }
            None => ("unknown".to_string(), " ".to_string(), "#FFFFFF"),
        }
    };

    // Write out the bit grid as HTML
    writeln!(
        html,
        "
"
    )
    .unwrap();
    for bit in 0..nbits {
        writeln!(html, "
").unwrap();
        for frame in 0..nframes {
            let (group, label, colour) = get_bit_info(frame, bit);
            writeln!(html, "").unwrap();
            let mut title = format!("F{}B{}", frame, bit);
            for funclabel in bitgrid[frame][bit].0.iter() {
                title.push('\n');
                title.push_str(funclabel);
            }
            writeln!(html, "\n\

{l}
	",
                    g=group, t=title, f=frame, b=bit, c=colour, l=label).unwrap();
        }
        writeln!(html, "
").unwrap();
    }
    writeln!(html, "
").unwrap();
    // Write out muxes as html
    for (sink, data) in bitdb.pips.iter() {
        writeln!(
            html,
            "
Mux driving {s}
",
            s = sink
        )
        .unwrap();
        writeln!(html, "

Source	").unwrap();
        let bitset: BTreeSet<(usize, usize)> = data
            .iter()
            .flat_map(|x| x.bits.iter())
            .map(|x| (x.frame, x.bit))
            .collect();
        for (frame, bit) in bitset.iter() {
            writeln!(
                html,
                "F{}B{}	",
                frame, bit
            )
            .unwrap();
        }
        writeln!(html, "
").unwrap();
        // Determine mux setting "truth table"
        let mut truthtable: Vec<(String, Vec<char>)> = data
            .iter()
            .map(|x| {
                (
                    x.from_wire.to_string(),
                    bitset
                        .iter()
                        .map(|&(f, b)| {
                            if x.bits.contains(&ConfigBit {
                                frame: f,
                                bit: b,
                                invert: false,
                            }) {
                                '1'
                            } else if x.bits.contains(&ConfigBit {
                                frame: f,
                                bit: b,
                                invert: true,
                            }) {
                                '0'
                            } else {
                                '-'
                            }
                        })
                        .collect(),
                )
            })
            .collect();

        let truth_key = |x: &[char]| -> String {
            String::from_iter(x.iter().map(|&c| match c {
                '-' => '0',
                o => o,
            }))
        };

        truthtable
            .sort_by(|(_n1, b1), (_n2, b2)| truth_key(b1).partial_cmp(&truth_key(b2)).unwrap());

        for (i, (from_wire, ttrow)) in truthtable.iter().enumerate() {
            let style = match i % 2 {
                0 => " bgcolor=\"#dddddd\"",
                _ => "",
            };
            writeln!(html, "
{f}	", s = style, f = from_wire).unwrap();
            for bit in ttrow.iter() {
                writeln!(html, "{}	", bit).unwrap();
            }
            writeln!(html, "	").unwrap();
        }
        writeln!(html, "
").unwrap();
    }
    writeln!(html, "").unwrap();
}
<commit_msg>Add words and enums to tile bits HTML output<commit_after>use crate::database::*;
use std::cmp::{max, min};
use std::collections::BTreeSet;
use std::fs::File;
use std::io::Write;
use std::iter::FromIterator;

// Get the shade colour for a tile type
fn get_colour(ttype: &str) -> &'static str {
    if ttype.contains("TAP") {
        return "#DDDDDD";
    } else if ttype.starts_with("SYSIO") {
        return "#88FF88";
    } else if ttype.starts_with("CIB") {
        return "#FF8888";
    } else if ttype.starts_with("PLC") {
        return "#8888FF";
    } else if ttype.starts_with("DUMMY") {
        return "#FFFFFF";
    } else if ttype.starts_with("MIB_EBR") || ttype.starts_with("EBR_") || ttype.starts_with("LRAM")
    {
        return "#FF88FF";
    } else if ttype.contains("DSP") || ttype.contains("ALU") {
        return "#FFFF88";
    } else if ttype.contains("PLL")
        || ttype.contains("DPHY")
        || ttype.contains("CDR")
        || ttype.contains("PCIE")
        || ttype.contains("EFB")
        || ttype.contains("PMU")
        || ttype.contains("ADC")
    {
        return "#88FFFF";
    } else {
        return "#888888";
    }
}

pub fn write_tilegrid_html(db: &mut Database, fam: &str, device: &str, filepath: &str) {
    let device_info = db.device_by_name(device).unwrap().2;
    let tilegrid = db.device_tilegrid(fam, device);
    let mut tiles: Vec<Vec<Vec<(&str, &str, &'static str)>>> =
        vec![vec![vec![]; (device_info.max_col + 1) as usize]; (device_info.max_row + 1) as usize];
    for (tileid, tiledata) in tilegrid.tiles.iter() {
        let name = tileid.split(':').next().unwrap();
        let colour = get_colour(&tiledata.tiletype);
        tiles[tiledata.y as usize][tiledata.x as usize].push((name, &tiledata.tiletype, colour));
    }
    let mut html = File::create(filepath).unwrap();
    write!(
        html,
        "\n\
<title>{d} Tiles</title>\n\
\n\

{d} Tilegrid
\n\

\n\
",
        d = device
    )
    .unwrap();
    for trow in tiles.iter() {
        writeln!(html, "
").unwrap();
        let row_max_height = trow.iter().map(Vec::len).fold(0, max);
        let row_height = max(75, 30 * row_max_height);
        for tloc in trow.iter() {
            writeln!(
                html,
                "",
                row_height
            )
            .unwrap();
            for (name, ttype, colour) in tloc.iter() {
                writeln!(html, "
{n}
{t}
",
                    h=(100.0 / (tloc.len() as f32)), c=colour, n=name, t=ttype).unwrap();
            }
            writeln!(html, "	").unwrap();
        }
        writeln!(html, "
").unwrap();
    }
}

// Get the colour given the first letter of  a wire label
fn wire_colour(c: char) -> &'static str {
    match c {
        'A' => "#88FFFF",
        'B' => "#FF88FF",
        'C' => "#8888FF",
        'D' => "#FFFF88",
        'M' => "#FFBBBB",
        'H' => "#BBBBFF",
        'V' => "#FFFFBB",
        _ => "#FF8888",
    }
}

pub fn write_bits_html(db: &mut Database, fam: &str, device: &str, tiletype: &str, filepath: &str) {
    let tilegrid = db.device_tilegrid(fam, device);
    let mut nframes = 0;
    let mut nbits = 0;
    for tile in tilegrid.tiles.values() {
        if tile.tiletype == tiletype {
            nframes = tile.frames;
            nbits = tile.bits;
            break;
        }
    }

    let bitdb = &db.tile_bitdb(fam, tiletype).db;

    // Find the purpose of each bit
    // (frame, bit) --> (link, labels)
    let mut bitgrid: Vec<Vec<(BTreeSet<String>, Option<String>)>> =
        vec![vec![(BTreeSet::new(), None); nbits]; nframes];
    for (wordname, data) in bitdb.words.iter() {
        for (i, bits) in data.bits.iter().enumerate() {
            for bit in bits {
                bitgrid[bit.frame][bit.bit]
                    .0
                    .insert(format!("{}[{}]", wordname, i));
                bitgrid[bit.frame][bit.bit].1 = Some(format!("word_{}", wordname));
            }
        }
    }
    for (enumname, data) in bitdb.enums.iter() {
        for bit in data.options.values().flatten() {
            bitgrid[bit.frame][bit.bit]
                .0
                .insert(format!("{}", enumname));
            bitgrid[bit.frame][bit.bit].1 = Some(format!("enum_{}", enumname));
        }
    }
    for (sink, data) in bitdb.pips.iter() {
        for bit in data.iter().map(|x| x.bits.iter()).flatten() {
            bitgrid[bit.frame][bit.bit].0.insert(format!("{}", sink));
            bitgrid[bit.frame][bit.bit].1 = Some(format!("mux_{}", sink));
        }
    }

    // Write out prelude HTML
    let mut html = File::create(filepath).unwrap();
    write!(
        html,
        " \n\
            <title>{} Bit Data</title>\n\
        ",
        tiletype
    )
    .unwrap();
    write!(
        html,
        "\n\
            \n\
            \n\
            "
    )
    .unwrap();

    // f, b -> (group, label, colour)
    let get_bit_info = |frame: usize, bit: usize| -> (String, String, &'static str) {
        match &bitgrid[frame][bit].1 {
            Some(group) => {
                let mut label = '?';
                let mut colour = "#FFFFFF";
                if group.starts_with("mux") {
                    let us_pos = group.rfind('_').unwrap();
                    label = group.chars().nth(us_pos + 1).unwrap();
                    if label == 'J' {
                        label = group.chars().nth(us_pos + 2).unwrap();
                    }
                    colour = wire_colour(label);
                } else if group.starts_with("enum") || group.starts_with("word") {
                    let us_pos = group.rfind(|c| c == '_' || c == '.').unwrap();
                    label = group.chars().nth(us_pos + 1).unwrap();
                    colour = "#88FF88";
                }
                (group.to_string(), label.to_string(), colour)
            }
            None => ("unknown".to_string(), " ".to_string(), "#FFFFFF"),
        }
    };

    // Write out the bit grid as HTML
    writeln!(
        html,
        "
"
    )
    .unwrap();
    for bit in 0..nbits {
        writeln!(html, "
").unwrap();
        for frame in 0..nframes {
            let (group, label, colour) = get_bit_info(frame, bit);
            writeln!(html, "").unwrap();
            let mut title = format!("F{}B{}", frame, bit);
            for funclabel in bitgrid[frame][bit].0.iter() {
                title.push('\n');
                title.push_str(funclabel);
            }
            writeln!(html, "\n\

{l}
	",
                    g=group, t=title, f=frame, b=bit, c=colour, l=label).unwrap();
        }
        writeln!(html, "
").unwrap();
    }
    writeln!(html, "
").unwrap();
    // Write out muxes as html
    for (sink, data) in bitdb.pips.iter() {
        writeln!(
            html,
            "
Mux driving {s}
",
            s = sink
        )
        .unwrap();
        writeln!(html, "

Source	").unwrap();
        let bitset: BTreeSet<(usize, usize)> = data
            .iter()
            .flat_map(|x| x.bits.iter())
            .map(|x| (x.frame, x.bit))
            .collect();
        for (frame, bit) in bitset.iter() {
            writeln!(
                html,
                "F{}B{}	",
                frame, bit
            )
            .unwrap();
        }
        writeln!(html, "
").unwrap();
        // Determine mux setting "truth table"
        let mut truthtable: Vec<(String, Vec<char>)> = data
            .iter()
            .map(|x| {
                (
                    x.from_wire.to_string(),
                    bitset
                        .iter()
                        .map(|&(f, b)| {
                            if x.bits.contains(&ConfigBit {
                                frame: f,
                                bit: b,
                                invert: false,
                            }) {
                                '1'
                            } else if x.bits.contains(&ConfigBit {
                                frame: f,
                                bit: b,
                                invert: true,
                            }) {
                                '0'
                            } else {
                                '-'
                            }
                        })
                        .collect(),
                )
            })
            .collect();

        let truth_key = |x: &[char]| -> String {
            String::from_iter(x.iter().map(|&c| match c {
                '-' => '0',
                o => o,
            }))
        };

        truthtable
            .sort_by(|(_n1, b1), (_n2, b2)| truth_key(b1).partial_cmp(&truth_key(b2)).unwrap());

        for (i, (from_wire, ttrow)) in truthtable.iter().enumerate() {
            let style = match i % 2 {
                0 => " bgcolor=\"#dddddd\"",
                _ => "",
            };
            writeln!(html, "
{f}	", s = style, f = from_wire).unwrap();
            for bit in ttrow.iter() {
                writeln!(html, "{}	", bit).unwrap();
            }
            writeln!(html, "
").unwrap();
        }
        writeln!(html, "
").unwrap();
    }

    // Write out words as HTML
    for (word, data) in bitdb.words.iter() {
        writeln!(
            html,
            "
Configuration word {w}
",
            w = word
        )
        .unwrap();
        writeln!(html, "
").unwrap();
        for (i, bits) in data.bits.iter().enumerate() {
            let style = match i % 2 {
                0 => " bgcolor=\"#dddddd\"",
                _ => "",
            };
            let cbits: Vec<String> = bits
                .iter()
                .map(|b| {
                    format!(
                        "{}F{}B{}",
                        match b.invert {
                            true => "!",
                            false => "",
                        },
                        b.frame,
                        b.bit
                    )
                })
                .collect();
            writeln!(html, "
{n}[{i}]	{b}
"
                , s=style, n=word, i=i, b=cbits.join(" ")).unwrap();
        }
        writeln!(html, "
").unwrap();
    }

    // Write out enums as HTML
    for (en, data) in bitdb.enums.iter() {
        writeln!(
            html,
            "
Configuration enum {e}
",
            e = en
        )
        .unwrap();
        writeln!(html, "

Value	").unwrap();
        let bitset: BTreeSet<(usize, usize)> = data
            .options
            .values()
            .flat_map(|x| x.iter())
            .map(|x| (x.frame, x.bit))
            .collect();
        for (frame, bit) in bitset.iter() {
            writeln!(
                html,
                "F{}B{}	",
                frame, bit
            )
            .unwrap();
        }
        writeln!(html, "
").unwrap();
        // Determine enum option "truth table"
        let truthtable: Vec<(String, Vec<char>)> = data
            .options
            .iter()
            .map(|(opt, bits)| {
                (
                    opt.to_string(),
                    bitset
                        .iter()
                        .map(|&(f, b)| {
                            if bits.contains(&ConfigBit {
                                frame: f,
                                bit: b,
                                invert: false,
                            }) {
                                '1'
                            } else if bits.contains(&ConfigBit {
                                frame: f,
                                bit: b,
                                invert: true,
                            }) {
                                '0'
                            } else {
                                '-'
                            }
                        })
                        .collect(),
                )
            })
            .collect();
        for (i, (opt, ttrow)) in truthtable.iter().enumerate() {
            let style = match i % 2 {
                0 => " bgcolor=\"#dddddd\"",
                _ => "",
            };
            writeln!(html, "
{f}	", s = style, f = opt).unwrap();
            for bit in ttrow.iter() {
                writeln!(html, "{}	", bit).unwrap();
            }
            writeln!(html, "
").unwrap();
        }
        writeln!(html, "
").unwrap();
    }

    writeln!(html, "").unwrap();
}
<|endoftext|>


<commit_before>use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use std::cmp::min;
use std::fmt;
use std::io;

/// VersionInfo is information about a Lucet module to allow the Lucet runtime to determine if or
/// how the module can be loaded, if so requested. The information here describes implementation
/// details in runtime support for `lucetc`-produced modules, and nothing higher level.
#[repr(C)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct VersionInfo {
    major: u16,
    minor: u16,
    patch: u16,
    reserved: u16,
    /// `version_hash` is either all nulls or the first eight ascii characters of the git commit
    /// hash of wherever this Version is coming from. In the case of a compiled lucet module, this
    /// hash will come from the git commit that the lucetc producing it came from. In a runtime
    /// context, it will be the git commit of lucet-runtime built into the embedder.
    ///
    /// The version hash will typically populated only in release builds, but may blank even in
    /// that case: if building from a packaged crate, or in a build environment that does not have
    /// "git" installed, `lucetc` and `lucet-runtime` will fall back to an empty hash.
    version_hash: [u8; 8],
}

impl fmt::Display for VersionInfo {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt, "{}.{}.{}", self.major, self.minor, self.patch)?;
        if u64::from_ne_bytes(self.version_hash) != 0 {
            write!(
                fmt,
                "-{}",
                std::str::from_utf8(&self.version_hash).unwrap_or("INVALID")
            )?;
        }
        Ok(())
    }
}

impl VersionInfo {
    pub fn new(major: u16, minor: u16, patch: u16, version_hash: [u8; 8]) -> VersionInfo {
        VersionInfo {
            major, minor, patch, reserved: 0x8000, version_hash
        }
    }

    /// A more permissive version check than for version equality. This check will allow a more
    /// precise `other`
    pub fn compatible_with(&self, other: &VersionInfo) -> bool {
        if !(self.valid() || other.valid()) {
            return false;
        }

        if self.major == other.major && self.minor == other.minor && self.patch == other.patch {
            if self.version_hash == [0u8; 8] {
                // we aren't bound to a specific git commit, so anything goes.
                true
            } else {
                self.version_hash == other.version_hash
            }
        } else {
            false
        }
    }

    pub fn write_to<W: WriteBytesExt>(&self, w: &mut W) -> io::Result<()> {
        w.write_u16::<LittleEndian>(self.major)?;
        w.write_u16::<LittleEndian>(self.minor)?;
        w.write_u16::<LittleEndian>(self.patch)?;
        w.write_u16::<LittleEndian>(self.reserved)?;
        w.write(&self.version_hash).and_then(|written| {
            if written != self.version_hash.len() {
                Err(io::Error::new(
                    io::ErrorKind::Other,
                    "unable to write full version hash",
                ))
            } else {
                Ok(())
            }
        })
    }

    pub fn read_from<R: ReadBytesExt>(r: &mut R) -> io::Result<Self> {
        let mut version_hash = [0u8; 8];
        Ok(VersionInfo {
            major: r.read_u16::<LittleEndian>()?,
            minor: r.read_u16::<LittleEndian>()?,
            patch: r.read_u16::<LittleEndian>()?,
            reserved: r.read_u16::<LittleEndian>()?,
            version_hash: {
                r.read_exact(&mut version_hash)?;
                version_hash
            },
        })
    }

    pub fn valid(&self) -> bool {
        self.reserved == 0x8000
    }

    pub fn current(current_hash: &'static [u8]) -> Self {
        let mut version_hash = [0u8; 8];

        for i in 0..min(version_hash.len(), current_hash.len()) {
            version_hash[i] = current_hash[i];
        }

        // The reasoning for this is as follows:
        // `SerializedModule`, in version before version information was introduced, began with a
        // pointer - `module_data_ptr`. This pointer would be relocated to somewhere in user space
        // for the embedder of `lucet-runtime`. On x86_64, hopefully, that's userland code in some
        // OS, meaning the pointer will be a pointer to user memory, and will be below
        // 0x8000_0000_0000_0000. By setting `reserved` to `0x8000`, we set what would be the
        // highest bit in `module_data_ptr` in an old `lucet-runtime` and guarantee a segmentation
        // fault when loading these newer modules with version information.
        VersionInfo::new(
            env!("CARGO_PKG_VERSION_MAJOR").parse().unwrap(),
            env!("CARGO_PKG_VERSION_MINOR").parse().unwrap(),
            env!("CARGO_PKG_VERSION_PATCH").parse().unwrap(),
            version_hash,
        )
    }
}
<commit_msg>grammar<commit_after>use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use std::cmp::min;
use std::fmt;
use std::io;

/// VersionInfo is information about a Lucet module to allow the Lucet runtime to determine if or
/// how the module can be loaded, if so requested. The information here describes implementation
/// details in runtime support for `lucetc`-produced modules, and nothing higher level.
#[repr(C)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct VersionInfo {
    major: u16,
    minor: u16,
    patch: u16,
    reserved: u16,
    /// `version_hash` is either all nulls or the first eight ascii characters of the git commit
    /// hash of wherever this Version is coming from. In the case of a compiled lucet module, this
    /// hash will come from the git commit that the lucetc producing it came from. In a runtime
    /// context, it will be the git commit of lucet-runtime built into the embedder.
    ///
    /// The version hash will typically populated only in release builds, but may blank even in
    /// that case: if building from a packaged crate, or in a build environment that does not have
    /// "git" installed, `lucetc` and `lucet-runtime` will fall back to an empty hash.
    version_hash: [u8; 8],
}

impl fmt::Display for VersionInfo {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt, "{}.{}.{}", self.major, self.minor, self.patch)?;
        if u64::from_ne_bytes(self.version_hash) != 0 {
            write!(
                fmt,
                "-{}",
                std::str::from_utf8(&self.version_hash).unwrap_or("INVALID")
            )?;
        }
        Ok(())
    }
}

impl VersionInfo {
    pub fn new(major: u16, minor: u16, patch: u16, version_hash: [u8; 8]) -> VersionInfo {
        VersionInfo {
            major, minor, patch, reserved: 0x8000, version_hash
        }
    }

    /// A more permissive version check than for version equality. This check will allow an `other`
    /// version that is more specific than `self`, but matches for data that is available.
    pub fn compatible_with(&self, other: &VersionInfo) -> bool {
        if !(self.valid() || other.valid()) {
            return false;
        }

        if self.major == other.major && self.minor == other.minor && self.patch == other.patch {
            if self.version_hash == [0u8; 8] {
                // we aren't bound to a specific git commit, so anything goes.
                true
            } else {
                self.version_hash == other.version_hash
            }
        } else {
            false
        }
    }

    pub fn write_to<W: WriteBytesExt>(&self, w: &mut W) -> io::Result<()> {
        w.write_u16::<LittleEndian>(self.major)?;
        w.write_u16::<LittleEndian>(self.minor)?;
        w.write_u16::<LittleEndian>(self.patch)?;
        w.write_u16::<LittleEndian>(self.reserved)?;
        w.write(&self.version_hash).and_then(|written| {
            if written != self.version_hash.len() {
                Err(io::Error::new(
                    io::ErrorKind::Other,
                    "unable to write full version hash",
                ))
            } else {
                Ok(())
            }
        })
    }

    pub fn read_from<R: ReadBytesExt>(r: &mut R) -> io::Result<Self> {
        let mut version_hash = [0u8; 8];
        Ok(VersionInfo {
            major: r.read_u16::<LittleEndian>()?,
            minor: r.read_u16::<LittleEndian>()?,
            patch: r.read_u16::<LittleEndian>()?,
            reserved: r.read_u16::<LittleEndian>()?,
            version_hash: {
                r.read_exact(&mut version_hash)?;
                version_hash
            },
        })
    }

    pub fn valid(&self) -> bool {
        self.reserved == 0x8000
    }

    pub fn current(current_hash: &'static [u8]) -> Self {
        let mut version_hash = [0u8; 8];

        for i in 0..min(version_hash.len(), current_hash.len()) {
            version_hash[i] = current_hash[i];
        }

        // The reasoning for this is as follows:
        // `SerializedModule`, in version before version information was introduced, began with a
        // pointer - `module_data_ptr`. This pointer would be relocated to somewhere in user space
        // for the embedder of `lucet-runtime`. On x86_64, hopefully, that's userland code in some
        // OS, meaning the pointer will be a pointer to user memory, and will be below
        // 0x8000_0000_0000_0000. By setting `reserved` to `0x8000`, we set what would be the
        // highest bit in `module_data_ptr` in an old `lucet-runtime` and guarantee a segmentation
        // fault when loading these newer modules with version information.
        VersionInfo::new(
            env!("CARGO_PKG_VERSION_MAJOR").parse().unwrap(),
            env!("CARGO_PKG_VERSION_MINOR").parse().unwrap(),
            env!("CARGO_PKG_VERSION_PATCH").parse().unwrap(),
            version_hash,
        )
    }
}
<|endoftext|>


<commit_before>use std::convert::From;
use std::error::Error as StdError;
use std::fmt::{self, Display};
use std::ffi::NulError;

#[derive(Debug)]
#[cfg_attr(feature = "clippy", allow(enum_variant_names))]
/// The generic "things can fail in a myriad of ways" enum. This type is not
/// indended to be exhaustively matched, and new variants may be added in the
/// future without a major version bump.
pub enum Error {
    InvalidCString(NulError),
    DatabaseError(DatabaseErrorKind, Box<DatabaseErrorInformation+Send+Sync>),
    NotFound,
    QueryBuilderError(Box<StdError+Send+Sync>),
    DeserializationError(Box<StdError+Send+Sync>),
    SerializationError(Box<StdError+Send+Sync>),
    /// You can return this variant inside of a transaction when you want to
    /// roll it back, but have no actual error to return. Diesel will never
    /// return this variant unless you gave it to us, and it can be safely
    /// ignored in error handling.
    RollbackTransaction,
    #[doc(hidden)]
    __Nonexhaustive,
}

#[derive(Debug, Clone, Copy)]
/// The kind of database error that occurred. This is not meant to exhaustively
/// cover all possible errors, but is used to identify errors which are commonly
/// recovered from programatically. This enum is not intended to be exhaustively
/// matched, and new variants may be added in the future without a major version
/// bump.
pub enum DatabaseErrorKind {
    UniqueViolation,
    ForeignKeyViolation,
    UnableToSendCommand,
    #[doc(hidden)]
    __Unknown, // Match against _ instead, more variants may be added in the future
}

pub trait DatabaseErrorInformation {
    fn message(&self) -> &str;
    fn details(&self) -> Option<&str>;
    fn hint(&self) -> Option<&str>;
    fn table_name(&self) -> Option<&str>;
    fn column_name(&self) -> Option<&str>;
    fn constraint_name(&self) -> Option<&str>;
}

impl fmt::Debug for DatabaseErrorInformation+Send+Sync {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.message(), f)
    }
}

impl DatabaseErrorInformation for String {
    fn message(&self) -> &str {
        self
    }

    fn details(&self) -> Option<&str> { None }
    fn hint(&self) -> Option<&str> { None }
    fn table_name(&self) -> Option<&str> { None }
    fn column_name(&self) -> Option<&str> { None }
    fn constraint_name(&self) -> Option<&str> { None }
}

#[derive(Debug)]
pub enum ConnectionError {
    InvalidCString(NulError),
    BadConnection(String),
    InvalidConnectionUrl(String),
    /// Diesel may try to automatically set session specific configuration
    /// values, such as UTF8 encoding, or enabling the `||` operator on MySQL.
    /// This variant is returned if an error occurred executing the query to set
    /// those options. Diesel will never affect global configuration.
    CouldntSetupConfiguration(Error),
    #[doc(hidden)]
    __Nonexhaustive, // Match against _ instead, more variants may be added in the future
}

pub type QueryResult<T> = Result<T, Error>;
pub type ConnectionResult<T> = Result<T, ConnectionError>;

pub trait OptionalExtension<T> {
    fn optional(self) -> Result<Option<T>, Error>;
}

impl<T> OptionalExtension<T> for QueryResult<T> {
    fn optional(self) -> Result<Option<T>, Error> {
        match self {
            Ok(value) => Ok(Some(value)),
            Err(Error::NotFound) => Ok(None),
            Err(e) => Err(e),
        }
    }
}

impl From<NulError> for ConnectionError {
    fn from(e: NulError) -> Self {
        ConnectionError::InvalidCString(e)
    }
}

impl From<NulError> for Error {
    fn from(e: NulError) -> Self {
        Error::InvalidCString(e)
    }
}

impl Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::InvalidCString(ref nul_err) => nul_err.fmt(f),
            Error::DatabaseError(_, ref e) => write!(f, "{}", e.message()),
            Error::NotFound => f.write_str("NotFound"),
            Error::QueryBuilderError(ref e) => e.fmt(f),
            Error::DeserializationError(ref e) => e.fmt(f),
            Error::SerializationError(ref e) => e.fmt(f),
            Error::RollbackTransaction => write!(f, "{}", self.description()),
            Error::__Nonexhaustive => unreachable!(),
        }
    }
}

impl StdError for Error {
    fn description(&self) -> &str {
        match *self {
            Error::InvalidCString(ref nul_err) => nul_err.description(),
            Error::DatabaseError(_, ref e) => e.message(),
            Error::NotFound => "Record not found",
            Error::QueryBuilderError(ref e) => e.description(),
            Error::DeserializationError(ref e) => e.description(),
            Error::SerializationError(ref e) => e.description(),
            Error::RollbackTransaction => "The current transaction was aborted",
            Error::__Nonexhaustive => unreachable!(),
        }
    }
}

impl Display for ConnectionError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ConnectionError::InvalidCString(ref nul_err) => nul_err.fmt(f),
            ConnectionError::BadConnection(ref s) => write!(f, "{}", s),
            ConnectionError::InvalidConnectionUrl(ref s) => write!(f, "{}", s),
            ConnectionError::CouldntSetupConfiguration(ref e) => e.fmt(f),
            ConnectionError::__Nonexhaustive => unreachable!(),
        }
    }
}

impl StdError for ConnectionError {
    fn description(&self) -> &str {
        match *self {
            ConnectionError::InvalidCString(ref nul_err) => nul_err.description(),
            ConnectionError::BadConnection(ref s) => s,
            ConnectionError::InvalidConnectionUrl(ref s) => s,
            ConnectionError::CouldntSetupConfiguration(ref e) => e.description(),
            ConnectionError::__Nonexhaustive => unreachable!(),
        }
    }
}

impl PartialEq for Error {
    fn eq(&self, other: &Error) -> bool {
        match (self, other) {
            (&Error::InvalidCString(ref a), &Error::InvalidCString(ref b)) => a == b,
            (&Error::DatabaseError(_, ref a), &Error::DatabaseError(_, ref b)) =>
                a.message() == b.message(),
            (&Error::NotFound, &Error::NotFound) => true,
            _ => false,
        }
    }
}

#[cfg(test)]
#[allow(warnings)]
fn error_impls_send() {
    let err: Error = unimplemented!();
    let x: &Send = &err;
}

pub(crate) fn first_or_not_found<T>(records: QueryResult<Vec<T>>) -> QueryResult<T> {
    records?.into_iter().next().ok_or(Error::NotFound)
}
<commit_msg>Document the `optional` method<commit_after>use std::convert::From;
use std::error::Error as StdError;
use std::fmt::{self, Display};
use std::ffi::NulError;

#[derive(Debug)]
#[cfg_attr(feature = "clippy", allow(enum_variant_names))]
/// The generic "things can fail in a myriad of ways" enum. This type is not
/// indended to be exhaustively matched, and new variants may be added in the
/// future without a major version bump.
pub enum Error {
    InvalidCString(NulError),
    DatabaseError(DatabaseErrorKind, Box<DatabaseErrorInformation+Send+Sync>),
    NotFound,
    QueryBuilderError(Box<StdError+Send+Sync>),
    DeserializationError(Box<StdError+Send+Sync>),
    SerializationError(Box<StdError+Send+Sync>),
    /// You can return this variant inside of a transaction when you want to
    /// roll it back, but have no actual error to return. Diesel will never
    /// return this variant unless you gave it to us, and it can be safely
    /// ignored in error handling.
    RollbackTransaction,
    #[doc(hidden)]
    __Nonexhaustive,
}

#[derive(Debug, Clone, Copy)]
/// The kind of database error that occurred. This is not meant to exhaustively
/// cover all possible errors, but is used to identify errors which are commonly
/// recovered from programatically. This enum is not intended to be exhaustively
/// matched, and new variants may be added in the future without a major version
/// bump.
pub enum DatabaseErrorKind {
    UniqueViolation,
    ForeignKeyViolation,
    UnableToSendCommand,
    #[doc(hidden)]
    __Unknown, // Match against _ instead, more variants may be added in the future
}

pub trait DatabaseErrorInformation {
    fn message(&self) -> &str;
    fn details(&self) -> Option<&str>;
    fn hint(&self) -> Option<&str>;
    fn table_name(&self) -> Option<&str>;
    fn column_name(&self) -> Option<&str>;
    fn constraint_name(&self) -> Option<&str>;
}

impl fmt::Debug for DatabaseErrorInformation+Send+Sync {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.message(), f)
    }
}

impl DatabaseErrorInformation for String {
    fn message(&self) -> &str {
        self
    }

    fn details(&self) -> Option<&str> { None }
    fn hint(&self) -> Option<&str> { None }
    fn table_name(&self) -> Option<&str> { None }
    fn column_name(&self) -> Option<&str> { None }
    fn constraint_name(&self) -> Option<&str> { None }
}

#[derive(Debug)]
pub enum ConnectionError {
    InvalidCString(NulError),
    BadConnection(String),
    InvalidConnectionUrl(String),
    /// Diesel may try to automatically set session specific configuration
    /// values, such as UTF8 encoding, or enabling the `||` operator on MySQL.
    /// This variant is returned if an error occurred executing the query to set
    /// those options. Diesel will never affect global configuration.
    CouldntSetupConfiguration(Error),
    #[doc(hidden)]
    __Nonexhaustive, // Match against _ instead, more variants may be added in the future
}

pub type QueryResult<T> = Result<T, Error>;
pub type ConnectionResult<T> = Result<T, ConnectionError>;

/// See the [method documentation](#tymethod.optional).
pub trait OptionalExtension<T> {
    /// Converts a `QueryResult<T>` into a `QueryResult<Option<T>>`.
    ///
    /// By default, Diesel treats 0 rows being returned from a query that is expected to return 1
    /// row as an error (e.g. the return value of [`get_result`] or [`first`]). This method will
    /// handle that error, and give you back an `Option<T>` instead.
    ///
    /// [`get_result`]: ../prelude/trait.LoadDsl.html#method.get_result
    /// [`first`]: ../prelude/trait.FirstDsl.html#method.first
    ///
    /// # Example
    ///
    /// ```rust
    /// use diesel::result::{QueryResult, Error};
    /// use diesel::OptionalExtension;
    ///
    /// let result: QueryResult<i32> = Ok(1);
    /// assert_eq!(Ok(Some(1)), result.optional());
    ///
    /// let result: QueryResult<i32> = Err(Error::NotFound);
    /// assert_eq!(Ok(None), result.optional());
    /// ```
    fn optional(self) -> Result<Option<T>, Error>;
}

impl<T> OptionalExtension<T> for QueryResult<T> {
    fn optional(self) -> Result<Option<T>, Error> {
        match self {
            Ok(value) => Ok(Some(value)),
            Err(Error::NotFound) => Ok(None),
            Err(e) => Err(e),
        }
    }
}

impl From<NulError> for ConnectionError {
    fn from(e: NulError) -> Self {
        ConnectionError::InvalidCString(e)
    }
}

impl From<NulError> for Error {
    fn from(e: NulError) -> Self {
        Error::InvalidCString(e)
    }
}

impl Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::InvalidCString(ref nul_err) => nul_err.fmt(f),
            Error::DatabaseError(_, ref e) => write!(f, "{}", e.message()),
            Error::NotFound => f.write_str("NotFound"),
            Error::QueryBuilderError(ref e) => e.fmt(f),
            Error::DeserializationError(ref e) => e.fmt(f),
            Error::SerializationError(ref e) => e.fmt(f),
            Error::RollbackTransaction => write!(f, "{}", self.description()),
            Error::__Nonexhaustive => unreachable!(),
        }
    }
}

impl StdError for Error {
    fn description(&self) -> &str {
        match *self {
            Error::InvalidCString(ref nul_err) => nul_err.description(),
            Error::DatabaseError(_, ref e) => e.message(),
            Error::NotFound => "Record not found",
            Error::QueryBuilderError(ref e) => e.description(),
            Error::DeserializationError(ref e) => e.description(),
            Error::SerializationError(ref e) => e.description(),
            Error::RollbackTransaction => "The current transaction was aborted",
            Error::__Nonexhaustive => unreachable!(),
        }
    }
}

impl Display for ConnectionError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ConnectionError::InvalidCString(ref nul_err) => nul_err.fmt(f),
            ConnectionError::BadConnection(ref s) => write!(f, "{}", s),
            ConnectionError::InvalidConnectionUrl(ref s) => write!(f, "{}", s),
            ConnectionError::CouldntSetupConfiguration(ref e) => e.fmt(f),
            ConnectionError::__Nonexhaustive => unreachable!(),
        }
    }
}

impl StdError for ConnectionError {
    fn description(&self) -> &str {
        match *self {
            ConnectionError::InvalidCString(ref nul_err) => nul_err.description(),
            ConnectionError::BadConnection(ref s) => s,
            ConnectionError::InvalidConnectionUrl(ref s) => s,
            ConnectionError::CouldntSetupConfiguration(ref e) => e.description(),
            ConnectionError::__Nonexhaustive => unreachable!(),
        }
    }
}

impl PartialEq for Error {
    fn eq(&self, other: &Error) -> bool {
        match (self, other) {
            (&Error::InvalidCString(ref a), &Error::InvalidCString(ref b)) => a == b,
            (&Error::DatabaseError(_, ref a), &Error::DatabaseError(_, ref b)) =>
                a.message() == b.message(),
            (&Error::NotFound, &Error::NotFound) => true,
            _ => false,
        }
    }
}

#[cfg(test)]
#[allow(warnings)]
fn error_impls_send() {
    let err: Error = unimplemented!();
    let x: &Send = &err;
}

pub(crate) fn first_or_not_found<T>(records: QueryResult<Vec<T>>) -> QueryResult<T> {
    records?.into_iter().next().ok_or(Error::NotFound)
}
<|endoftext|>


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

#![allow(unsafe_blocks)]

use construct::ConstructionResult;
use incremental::RestyleDamage;
use parallel::DomParallelInfo;
use wrapper::{LayoutNode, TLayoutNode, ThreadSafeLayoutNode};

use gfx::display_list::OpaqueNode;
use gfx;
use libc::uintptr_t;
use script::dom::bindings::js::LayoutJS;
use script::dom::node::{Node, SharedLayoutData};
use script::layout_interface::{LayoutChan, TrustedNodeAddress};
use script_traits::UntrustedNodeAddress;
use std::mem;
use std::cell::{Ref, RefMut};
use style::properties::ComputedValues;
use style;
use std::sync::Arc;

/// Data that layout associates with a node.
pub struct PrivateLayoutData {
    /// The results of CSS styling for this node's `before` pseudo-element, if any.
    pub before_style: Option<Arc<ComputedValues>>,

    /// The results of CSS styling for this node's `after` pseudo-element, if any.
    pub after_style: Option<Arc<ComputedValues>>,

    /// Description of how to account for recent style changes.
    pub restyle_damage: RestyleDamage,

    /// The current results of flow construction for this node. This is either a flow or a
    /// `ConstructionItem`. See comments in `construct.rs` for more details.
    pub flow_construction_result: ConstructionResult,

    pub before_flow_construction_result: ConstructionResult,

    pub after_flow_construction_result: ConstructionResult,

    /// Information needed during parallel traversals.
    pub parallel: DomParallelInfo,

    /// Various flags.
    pub flags: LayoutDataFlags,
}

impl PrivateLayoutData {
    /// Creates new layout data.
    pub fn new() -> PrivateLayoutData {
        PrivateLayoutData {
            before_style: None,
            after_style: None,
            restyle_damage: RestyleDamage::empty(),
            flow_construction_result: ConstructionResult::None,
            before_flow_construction_result: ConstructionResult::None,
            after_flow_construction_result: ConstructionResult::None,
            parallel: DomParallelInfo::new(),
            flags: LayoutDataFlags::empty(),
        }
    }
}

bitflags! {
    flags LayoutDataFlags: u8 {
        #[doc="Whether a flow has been newly constructed."]
        const HAS_NEWLY_CONSTRUCTED_FLOW = 0x01
    }
}

pub struct LayoutDataWrapper {
    pub chan: Option<LayoutChan>,
    pub shared_data: SharedLayoutData,
    pub data: Box<PrivateLayoutData>,
}

#[allow(dead_code)]
fn static_assertion(x: Option<LayoutDataWrapper>) {
    unsafe {
        let _: Option<::script::dom::node::LayoutData> =
            ::std::intrinsics::transmute(x);
    }
}

/// A trait that allows access to the layout data of a DOM node.
pub trait LayoutDataAccess {
    /// Borrows the layout data without checks.
    unsafe fn borrow_layout_data_unchecked(&self) -> *const Option<LayoutDataWrapper>;
    /// Borrows the layout data immutably. Fails on a conflicting borrow.
    fn borrow_layout_data<'a>(&'a self) -> Ref<'a,Option<LayoutDataWrapper>>;
    /// Borrows the layout data mutably. Fails on a conflicting borrow.
    fn mutate_layout_data<'a>(&'a self) -> RefMut<'a,Option<LayoutDataWrapper>>;
}

impl<'ln> LayoutDataAccess for LayoutNode<'ln> {
    #[inline(always)]
    unsafe fn borrow_layout_data_unchecked(&self) -> *const Option<LayoutDataWrapper> {
        mem::transmute(self.get().layout_data_unchecked())
    }

    #[inline(always)]
    fn borrow_layout_data<'a>(&'a self) -> Ref<'a,Option<LayoutDataWrapper>> {
        unsafe {
            mem::transmute(self.get().layout_data())
        }
    }

    #[inline(always)]
    fn mutate_layout_data<'a>(&'a self) -> RefMut<'a,Option<LayoutDataWrapper>> {
        unsafe {
            mem::transmute(self.get().layout_data_mut())
        }
    }
}

pub trait OpaqueNodeMethods {
    /// Converts a DOM node (layout view) to an `OpaqueNode`.
    fn from_layout_node(node: &LayoutNode) -> Self;

    /// Converts a thread-safe DOM node (layout view) to an `OpaqueNode`.
    fn from_thread_safe_layout_node(node: &ThreadSafeLayoutNode) -> Self;

    /// Converts a DOM node (script view) to an `OpaqueNode`.
    fn from_script_node(node: TrustedNodeAddress) -> Self;

    /// Converts a DOM node to an `OpaqueNode'.
    fn from_jsmanaged(node: &LayoutJS<Node>) -> Self;

    /// Converts this node to an `UntrustedNodeAddress`. An `UntrustedNodeAddress` is just the type
    /// of node that script expects to receive in a hit test.
    fn to_untrusted_node_address(&self) -> UntrustedNodeAddress;
}

impl OpaqueNodeMethods for OpaqueNode {
    fn from_layout_node(node: &LayoutNode) -> OpaqueNode {
        unsafe {
            OpaqueNodeMethods::from_jsmanaged(node.get_jsmanaged())
        }
    }

    fn from_thread_safe_layout_node(node: &ThreadSafeLayoutNode) -> OpaqueNode {
        unsafe {
            let abstract_node = node.get_jsmanaged();
            let ptr: uintptr_t = abstract_node.get_jsobject() as uintptr_t;
            OpaqueNode(ptr)
        }
    }

    fn from_script_node(node: TrustedNodeAddress) -> OpaqueNode {
        unsafe {
            OpaqueNodeMethods::from_jsmanaged(&LayoutJS::from_trusted_node_address(node))
        }
    }

    fn from_jsmanaged(node: &LayoutJS<Node>) -> OpaqueNode {
        unsafe {
            let ptr: uintptr_t = node.get_jsobject() as uintptr_t;
            OpaqueNode(ptr)
        }
    }

    fn to_untrusted_node_address(&self) -> UntrustedNodeAddress {
        unsafe {
            let OpaqueNode(addr) = *self;
            let addr: UntrustedNodeAddress = mem::transmute(addr);
            addr
        }
    }
}

/// Allows a CSS color to be converted into a graphics color.
pub trait ToGfxColor {
    /// Converts a CSS color to a graphics color.
    fn to_gfx_color(&self) -> gfx::color::Color;
}

impl ToGfxColor for style::values::RGBA {
    fn to_gfx_color(&self) -> gfx::color::Color {
        gfx::color::rgba(self.red, self.green, self.blue, self.alpha)
    }
}
<commit_msg>Remove a transmute from to_untrusted_node_address.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#![allow(unsafe_blocks)]

use construct::ConstructionResult;
use incremental::RestyleDamage;
use parallel::DomParallelInfo;
use wrapper::{LayoutNode, TLayoutNode, ThreadSafeLayoutNode};

use gfx::display_list::OpaqueNode;
use gfx;
use libc::{c_void, uintptr_t};
use script::dom::bindings::js::LayoutJS;
use script::dom::node::{Node, SharedLayoutData};
use script::layout_interface::{LayoutChan, TrustedNodeAddress};
use script_traits::UntrustedNodeAddress;
use std::mem;
use std::cell::{Ref, RefMut};
use style::properties::ComputedValues;
use style;
use std::sync::Arc;

/// Data that layout associates with a node.
pub struct PrivateLayoutData {
    /// The results of CSS styling for this node's `before` pseudo-element, if any.
    pub before_style: Option<Arc<ComputedValues>>,

    /// The results of CSS styling for this node's `after` pseudo-element, if any.
    pub after_style: Option<Arc<ComputedValues>>,

    /// Description of how to account for recent style changes.
    pub restyle_damage: RestyleDamage,

    /// The current results of flow construction for this node. This is either a flow or a
    /// `ConstructionItem`. See comments in `construct.rs` for more details.
    pub flow_construction_result: ConstructionResult,

    pub before_flow_construction_result: ConstructionResult,

    pub after_flow_construction_result: ConstructionResult,

    /// Information needed during parallel traversals.
    pub parallel: DomParallelInfo,

    /// Various flags.
    pub flags: LayoutDataFlags,
}

impl PrivateLayoutData {
    /// Creates new layout data.
    pub fn new() -> PrivateLayoutData {
        PrivateLayoutData {
            before_style: None,
            after_style: None,
            restyle_damage: RestyleDamage::empty(),
            flow_construction_result: ConstructionResult::None,
            before_flow_construction_result: ConstructionResult::None,
            after_flow_construction_result: ConstructionResult::None,
            parallel: DomParallelInfo::new(),
            flags: LayoutDataFlags::empty(),
        }
    }
}

bitflags! {
    flags LayoutDataFlags: u8 {
        #[doc="Whether a flow has been newly constructed."]
        const HAS_NEWLY_CONSTRUCTED_FLOW = 0x01
    }
}

pub struct LayoutDataWrapper {
    pub chan: Option<LayoutChan>,
    pub shared_data: SharedLayoutData,
    pub data: Box<PrivateLayoutData>,
}

#[allow(dead_code)]
fn static_assertion(x: Option<LayoutDataWrapper>) {
    unsafe {
        let _: Option<::script::dom::node::LayoutData> =
            ::std::intrinsics::transmute(x);
    }
}

/// A trait that allows access to the layout data of a DOM node.
pub trait LayoutDataAccess {
    /// Borrows the layout data without checks.
    unsafe fn borrow_layout_data_unchecked(&self) -> *const Option<LayoutDataWrapper>;
    /// Borrows the layout data immutably. Fails on a conflicting borrow.
    fn borrow_layout_data<'a>(&'a self) -> Ref<'a,Option<LayoutDataWrapper>>;
    /// Borrows the layout data mutably. Fails on a conflicting borrow.
    fn mutate_layout_data<'a>(&'a self) -> RefMut<'a,Option<LayoutDataWrapper>>;
}

impl<'ln> LayoutDataAccess for LayoutNode<'ln> {
    #[inline(always)]
    unsafe fn borrow_layout_data_unchecked(&self) -> *const Option<LayoutDataWrapper> {
        mem::transmute(self.get().layout_data_unchecked())
    }

    #[inline(always)]
    fn borrow_layout_data<'a>(&'a self) -> Ref<'a,Option<LayoutDataWrapper>> {
        unsafe {
            mem::transmute(self.get().layout_data())
        }
    }

    #[inline(always)]
    fn mutate_layout_data<'a>(&'a self) -> RefMut<'a,Option<LayoutDataWrapper>> {
        unsafe {
            mem::transmute(self.get().layout_data_mut())
        }
    }
}

pub trait OpaqueNodeMethods {
    /// Converts a DOM node (layout view) to an `OpaqueNode`.
    fn from_layout_node(node: &LayoutNode) -> Self;

    /// Converts a thread-safe DOM node (layout view) to an `OpaqueNode`.
    fn from_thread_safe_layout_node(node: &ThreadSafeLayoutNode) -> Self;

    /// Converts a DOM node (script view) to an `OpaqueNode`.
    fn from_script_node(node: TrustedNodeAddress) -> Self;

    /// Converts a DOM node to an `OpaqueNode'.
    fn from_jsmanaged(node: &LayoutJS<Node>) -> Self;

    /// Converts this node to an `UntrustedNodeAddress`. An `UntrustedNodeAddress` is just the type
    /// of node that script expects to receive in a hit test.
    fn to_untrusted_node_address(&self) -> UntrustedNodeAddress;
}

impl OpaqueNodeMethods for OpaqueNode {
    fn from_layout_node(node: &LayoutNode) -> OpaqueNode {
        unsafe {
            OpaqueNodeMethods::from_jsmanaged(node.get_jsmanaged())
        }
    }

    fn from_thread_safe_layout_node(node: &ThreadSafeLayoutNode) -> OpaqueNode {
        unsafe {
            let abstract_node = node.get_jsmanaged();
            let ptr: uintptr_t = abstract_node.get_jsobject() as uintptr_t;
            OpaqueNode(ptr)
        }
    }

    fn from_script_node(node: TrustedNodeAddress) -> OpaqueNode {
        unsafe {
            OpaqueNodeMethods::from_jsmanaged(&LayoutJS::from_trusted_node_address(node))
        }
    }

    fn from_jsmanaged(node: &LayoutJS<Node>) -> OpaqueNode {
        unsafe {
            let ptr: uintptr_t = node.get_jsobject() as uintptr_t;
            OpaqueNode(ptr)
        }
    }

    fn to_untrusted_node_address(&self) -> UntrustedNodeAddress {
        let OpaqueNode(addr) = *self;
        UntrustedNodeAddress(addr as *const c_void)
    }
}

/// Allows a CSS color to be converted into a graphics color.
pub trait ToGfxColor {
    /// Converts a CSS color to a graphics color.
    fn to_gfx_color(&self) -> gfx::color::Color;
}

impl ToGfxColor for style::values::RGBA {
    fn to_gfx_color(&self) -> gfx::color::Color {
        gfx::color::rgba(self.red, self.green, self.blue, self.alpha)
    }
}
<|endoftext|>


<commit_before>// Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//! The mmap module provides a safe interface to mmap memory and ensures unmap is called when the
//! mmap object leaves scope.

use std;
use std::io::{Read, Write};
use std::ptr::null_mut;
use std::os::unix::io::AsRawFd;

use libc;

use errno;

use data_model::volatile_memory::*;
use data_model::DataInit;

#[derive(Debug)]
pub enum Error {
    /// Requested memory out of range.
    InvalidAddress,
    /// Requested memory range spans past the end of the region.
    InvalidRange(usize, usize),
    /// Couldn't read from the given source.
    ReadFromSource(std::io::Error),
    /// `mmap` returned the given error.
    SystemCallFailed(errno::Error),
    /// Writing to memory failed
    WriteToMemory(std::io::Error),
    /// Reading from memory failed
    ReadFromMemory(std::io::Error),
}
pub type Result<T> = std::result::Result<T, Error>;

/// Wraps an anonymous shared memory mapping in the current process.
pub struct MemoryMapping {
    addr: *mut u8,
    size: usize,
}

// Send and Sync aren't automatically inherited for the raw address pointer.
// Accessing that pointer is only done through the stateless interface which
// allows the object to be shared by multiple threads without a decrease in
// safety.
unsafe impl Send for MemoryMapping {}
unsafe impl Sync for MemoryMapping {}

impl MemoryMapping {
    /// Creates an anonymous shared mapping of `size` bytes.
    ///
    /// # Arguments
    /// * `size` - Size of memory region in bytes.
    pub fn new(size: usize) -> Result<MemoryMapping> {
        // This is safe because we are creating an anonymous mapping in a place not already used by
        // any other area in this process.
        let addr = unsafe {
            libc::mmap(
                null_mut(),
                size,
                libc::PROT_READ | libc::PROT_WRITE,
                libc::MAP_ANONYMOUS | libc::MAP_SHARED | libc::MAP_NORESERVE,
                -1,
                0,
            )
        };
        if addr.is_null() {
            return Err(Error::SystemCallFailed(errno::Error::last()));
        }
        Ok(MemoryMapping {
            addr: addr as *mut u8,
            size: size,
        })
    }

    /// Maps the first `size` bytes of the given `fd`.
    ///
    /// # Arguments
    /// * `fd` - File descriptor to mmap from.
    /// * `size` - Size of memory region in bytes.
    pub fn from_fd(fd: &AsRawFd, size: usize) -> Result<MemoryMapping> {
        // This is safe because we are creating a mapping in a place not already used by any other
        // area in this process.
        let addr = unsafe {
            libc::mmap(
                null_mut(),
                size,
                libc::PROT_READ | libc::PROT_WRITE,
                libc::MAP_SHARED,
                fd.as_raw_fd(),
                0,
            )
        };
        if addr.is_null() {
            return Err(Error::SystemCallFailed(errno::Error::last()));
        }
        Ok(MemoryMapping {
            addr: addr as *mut u8,
            size: size,
        })
    }

    /// Returns a pointer to the begining of the memory region.  Should only be
    /// used for passing this region to ioctls for setting guest memory.
    pub fn as_ptr(&self) -> *mut u8 {
        self.addr
    }

    /// Returns the size of the memory region in bytes.
    pub fn size(&self) -> usize {
        self.size
    }

    /// Writes a slice to the memory region at the specified offset.
    /// Returns the number of bytes written.  The number of bytes written can
    /// be less than the length of the slice if there isn't enough room in the
    /// memory region.
    ///
    /// # Examples
    /// * Write a slice at offset 256.
    ///
    /// ```
    /// #   use sys_util::MemoryMapping;
    /// #   let mut mem_map = MemoryMapping::new(1024).unwrap();
    ///     let res = mem_map.write_slice(&[1,2,3,4,5], 256);
    ///     assert!(res.is_ok());
    ///     assert_eq!(res.unwrap(), 5);
    /// ```
    pub fn write_slice(&self, buf: &[u8], offset: usize) -> Result<usize> {
        if offset >= self.size {
            return Err(Error::InvalidAddress);
        }
        unsafe {
            // Guest memory can't strictly be modeled as a slice because it is
            // volatile.  Writing to it with what compiles down to a memcpy
            // won't hurt anything as long as we get the bounds checks right.
            let mut slice: &mut [u8] = &mut self.as_mut_slice()[offset..];
            Ok(slice.write(buf).map_err(Error::WriteToMemory)?)
        }
    }

    /// Reads to a slice from the memory region at the specified offset.
    /// Returns the number of bytes read.  The number of bytes read can
    /// be less than the length of the slice if there isn't enough room in the
    /// memory region.
    ///
    /// # Examples
    /// * Read a slice of size 16 at offset 256.
    ///
    /// ```
    /// #   use sys_util::MemoryMapping;
    /// #   let mut mem_map = MemoryMapping::new(1024).unwrap();
    ///     let buf = &mut [0u8; 16];
    ///     let res = mem_map.read_slice(buf, 256);
    ///     assert!(res.is_ok());
    ///     assert_eq!(res.unwrap(), 16);
    /// ```
    pub fn read_slice(&self, mut buf: &mut [u8], offset: usize) -> Result<usize> {
        if offset >= self.size {
            return Err(Error::InvalidAddress);
        }
        unsafe {
            // Guest memory can't strictly be modeled as a slice because it is
            // volatile.  Writing to it with what compiles down to a memcpy
            // won't hurt anything as long as we get the bounds checks right.
            let slice: &[u8] = &self.as_slice()[offset..];
            Ok(buf.write(slice).map_err(Error::ReadFromMemory)?)
        }
    }

    /// Writes an object to the memory region at the specified offset.
    /// Returns Ok(()) if the object fits, or Err if it extends past the end.
    ///
    /// # Examples
    /// * Write a u64 at offset 16.
    ///
    /// ```
    /// #   use sys_util::MemoryMapping;
    /// #   let mut mem_map = MemoryMapping::new(1024).unwrap();
    ///     let res = mem_map.write_obj(55u64, 16);
    ///     assert!(res.is_ok());
    /// ```
    pub fn write_obj<T: DataInit>(&self, val: T, offset: usize) -> Result<()> {
        unsafe {
            // Guest memory can't strictly be modeled as a slice because it is
            // volatile.  Writing to it with what compiles down to a memcpy
            // won't hurt anything as long as we get the bounds checks right.
            if offset + std::mem::size_of::<T>() > self.size {
                return Err(Error::InvalidAddress);
            }
            std::ptr::write_volatile(&mut self.as_mut_slice()[offset..] as *mut _ as *mut T, val);
            Ok(())
        }
    }

    /// Reads on object from the memory region at the given offset.
    /// Reading from a volatile area isn't strictly safe as it could change
    /// mid-read.  However, as long as the type T is plain old data and can
    /// handle random initialization, everything will be OK.
    ///
    /// # Examples
    /// * Read a u64 written to offset 32.
    ///
    /// ```
    /// #   use sys_util::MemoryMapping;
    /// #   let mut mem_map = MemoryMapping::new(1024).unwrap();
    ///     let res = mem_map.write_obj(55u64, 32);
    ///     assert!(res.is_ok());
    ///     let num: u64 = mem_map.read_obj(32).unwrap();
    ///     assert_eq!(55, num);
    /// ```
    pub fn read_obj<T: DataInit>(&self, offset: usize) -> Result<T> {
        if offset + std::mem::size_of::<T>() > self.size {
            return Err(Error::InvalidAddress);
        }
        unsafe {
            // This is safe because by definition Copy types can have their bits
            // set arbitrarily and still be valid.
            Ok(std::ptr::read_volatile(
                &self.as_slice()[offset..] as *const _ as *const T,
            ))
        }
    }

    /// Reads data from a readable object like a File and writes it to guest memory.
    ///
    /// # Arguments
    /// * `mem_offset` - Begin writing memory at this offset.
    /// * `src` - Read from `src` to memory.
    /// * `count` - Read `count` bytes from `src` to memory.
    ///
    /// # Examples
    ///
    /// * Read bytes from /dev/urandom
    ///
    /// ```
    /// # use sys_util::MemoryMapping;
    /// # use std::fs::File;
    /// # use std::path::Path;
    /// # fn test_read_random() -> Result<u32, ()> {
    /// #     let mut mem_map = MemoryMapping::new(1024).unwrap();
    ///       let mut file = File::open(Path::new("/dev/urandom")).map_err(|_| ())?;
    ///       mem_map.read_to_memory(32, &mut file, 128).map_err(|_| ())?;
    ///       let rand_val: u32 =  mem_map.read_obj(40).map_err(|_| ())?;
    /// #     Ok(rand_val)
    /// # }
    /// ```
    pub fn read_to_memory<F>(&self, mem_offset: usize, src: &mut F, count: usize) -> Result<()>
    where
        F: Read,
    {
        let mem_end = mem_offset + count;
        if mem_end > self.size() {
            return Err(Error::InvalidRange(mem_offset, count));
        }
        unsafe {
            // It is safe to overwrite the volatile memory.  Acessing the guest
            // memory as a mutable slice is OK because nothing assumes another
            // thread won't change what is loaded.
            let dst = &mut self.as_mut_slice()[mem_offset..mem_end];
            src.read_exact(dst).map_err(Error::ReadFromSource)?;
        }
        Ok(())
    }

    /// Writes data from memory to a writable object.
    ///
    /// # Arguments
    /// * `mem_offset` - Begin reading memory from this offset.
    /// * `dst` - Write from memory to `dst`.
    /// * `count` - Read `count` bytes from memory to `src`.
    ///
    /// # Examples
    ///
    /// * Write 128 bytes to /dev/null
    ///
    /// ```
    /// # use sys_util::MemoryMapping;
    /// # use std::fs::File;
    /// # use std::path::Path;
    /// # fn test_write_null() -> Result<(), ()> {
    /// #     let mut mem_map = MemoryMapping::new(1024).unwrap();
    ///       let mut file = File::open(Path::new("/dev/null")).map_err(|_| ())?;
    ///       mem_map.write_from_memory(32, &mut file, 128).map_err(|_| ())?;
    /// #     Ok(())
    /// # }
    /// ```
    pub fn write_from_memory<F>(&self, mem_offset: usize, dst: &mut F, count: usize) -> Result<()>
    where
        F: Write,
    {
        let mem_end = match mem_offset.checked_add(count) {
            None => return Err(Error::InvalidRange(mem_offset, count)),
            Some(m) => m,
        };
        if mem_end > self.size() {
            return Err(Error::InvalidRange(mem_offset, count));
        }
        unsafe {
            // It is safe to read from volatile memory.  Acessing the guest
            // memory as a slice is OK because nothing assumes another thread
            // won't change what is loaded.
            let src = &self.as_mut_slice()[mem_offset..mem_end];
            dst.write_all(src).map_err(Error::ReadFromSource)?;
        }
        Ok(())
    }

    unsafe fn as_slice(&self) -> &[u8] {
        // This is safe because we mapped the area at addr ourselves, so this slice will not
        // overflow. However, it is possible to alias.
        std::slice::from_raw_parts(self.addr, self.size)
    }

    unsafe fn as_mut_slice(&self) -> &mut [u8] {
        // This is safe because we mapped the area at addr ourselves, so this slice will not
        // overflow. However, it is possible to alias.
        std::slice::from_raw_parts_mut(self.addr, self.size)
    }
}

impl VolatileMemory for MemoryMapping {
    fn get_slice(&self, offset: usize, count: usize) -> VolatileMemoryResult<VolatileSlice> {
        let mem_end = calc_offset(offset, count)?;
        if mem_end > self.size {
            return Err(VolatileMemoryError::OutOfBounds { addr: mem_end });
        }

        // Safe because we checked that offset + count was within our range and we only ever hand
        // out volatile accessors.
        Ok(unsafe { VolatileSlice::new((self.addr as usize + offset) as *mut _, count) })
    }
}

impl Drop for MemoryMapping {
    fn drop(&mut self) {
        // This is safe because we mmap the area at addr ourselves, and nobody
        // else is holding a reference to it.
        unsafe {
            libc::munmap(self.addr as *mut libc::c_void, self.size);
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use data_model::{VolatileMemory, VolatileMemoryError};

    #[test]
    fn basic_map() {
        let m = MemoryMapping::new(1024).unwrap();
        assert_eq!(1024, m.size());
    }

    #[test]
    fn test_write_past_end() {
        let m = MemoryMapping::new(5).unwrap();
        let res = m.write_slice(&[1, 2, 3, 4, 5, 6], 0);
        assert!(res.is_ok());
        assert_eq!(res.unwrap(), 5);
    }

    #[test]
    fn slice_size() {
        let m = MemoryMapping::new(5).unwrap();
        let s = m.get_slice(2, 3).unwrap();
        assert_eq!(s.size(), 3);
    }

    #[test]
    fn slice_addr() {
        let m = MemoryMapping::new(5).unwrap();
        let s = m.get_slice(2, 3).unwrap();
        assert_eq!(s.as_ptr(), unsafe { m.as_ptr().offset(2) });
    }

    #[test]
    fn slice_store() {
        let m = MemoryMapping::new(5).unwrap();
        let r = m.get_ref(2).unwrap();
        r.store(9u16);
        assert_eq!(m.read_obj::<u16>(2).unwrap(), 9);
    }

    #[test]
    fn slice_overflow_error() {
        use std::usize;
        let m = MemoryMapping::new(5).unwrap();
        let res = m.get_slice(usize::MAX, 3).unwrap_err();
        assert_eq!(
            res,
            VolatileMemoryError::Overflow {
                base: usize::MAX,
                offset: 3,
            }
        );
    }
    #[test]
    fn slice_oob_error() {
        let m = MemoryMapping::new(5).unwrap();
        let res = m.get_slice(3, 3).unwrap_err();
        assert_eq!(res, VolatileMemoryError::OutOfBounds { addr: 6 });
    }
}
<commit_msg>sys_util: implement Debug trait for MemoryMapping<commit_after>// Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//! The mmap module provides a safe interface to mmap memory and ensures unmap is called when the
//! mmap object leaves scope.

use std;
use std::io::{Read, Write};
use std::ptr::null_mut;
use std::os::unix::io::AsRawFd;

use libc;

use errno;

use data_model::volatile_memory::*;
use data_model::DataInit;

#[derive(Debug)]
pub enum Error {
    /// Requested memory out of range.
    InvalidAddress,
    /// Requested memory range spans past the end of the region.
    InvalidRange(usize, usize),
    /// Couldn't read from the given source.
    ReadFromSource(std::io::Error),
    /// `mmap` returned the given error.
    SystemCallFailed(errno::Error),
    /// Writing to memory failed
    WriteToMemory(std::io::Error),
    /// Reading from memory failed
    ReadFromMemory(std::io::Error),
}
pub type Result<T> = std::result::Result<T, Error>;

/// Wraps an anonymous shared memory mapping in the current process.
#[derive(Debug)]
pub struct MemoryMapping {
    addr: *mut u8,
    size: usize,
}

// Send and Sync aren't automatically inherited for the raw address pointer.
// Accessing that pointer is only done through the stateless interface which
// allows the object to be shared by multiple threads without a decrease in
// safety.
unsafe impl Send for MemoryMapping {}
unsafe impl Sync for MemoryMapping {}

impl MemoryMapping {
    /// Creates an anonymous shared mapping of `size` bytes.
    ///
    /// # Arguments
    /// * `size` - Size of memory region in bytes.
    pub fn new(size: usize) -> Result<MemoryMapping> {
        // This is safe because we are creating an anonymous mapping in a place not already used by
        // any other area in this process.
        let addr = unsafe {
            libc::mmap(
                null_mut(),
                size,
                libc::PROT_READ | libc::PROT_WRITE,
                libc::MAP_ANONYMOUS | libc::MAP_SHARED | libc::MAP_NORESERVE,
                -1,
                0,
            )
        };
        if addr.is_null() {
            return Err(Error::SystemCallFailed(errno::Error::last()));
        }
        Ok(MemoryMapping {
            addr: addr as *mut u8,
            size: size,
        })
    }

    /// Maps the first `size` bytes of the given `fd`.
    ///
    /// # Arguments
    /// * `fd` - File descriptor to mmap from.
    /// * `size` - Size of memory region in bytes.
    pub fn from_fd(fd: &AsRawFd, size: usize) -> Result<MemoryMapping> {
        // This is safe because we are creating a mapping in a place not already used by any other
        // area in this process.
        let addr = unsafe {
            libc::mmap(
                null_mut(),
                size,
                libc::PROT_READ | libc::PROT_WRITE,
                libc::MAP_SHARED,
                fd.as_raw_fd(),
                0,
            )
        };
        if addr.is_null() {
            return Err(Error::SystemCallFailed(errno::Error::last()));
        }
        Ok(MemoryMapping {
            addr: addr as *mut u8,
            size: size,
        })
    }

    /// Returns a pointer to the begining of the memory region.  Should only be
    /// used for passing this region to ioctls for setting guest memory.
    pub fn as_ptr(&self) -> *mut u8 {
        self.addr
    }

    /// Returns the size of the memory region in bytes.
    pub fn size(&self) -> usize {
        self.size
    }

    /// Writes a slice to the memory region at the specified offset.
    /// Returns the number of bytes written.  The number of bytes written can
    /// be less than the length of the slice if there isn't enough room in the
    /// memory region.
    ///
    /// # Examples
    /// * Write a slice at offset 256.
    ///
    /// ```
    /// #   use sys_util::MemoryMapping;
    /// #   let mut mem_map = MemoryMapping::new(1024).unwrap();
    ///     let res = mem_map.write_slice(&[1,2,3,4,5], 256);
    ///     assert!(res.is_ok());
    ///     assert_eq!(res.unwrap(), 5);
    /// ```
    pub fn write_slice(&self, buf: &[u8], offset: usize) -> Result<usize> {
        if offset >= self.size {
            return Err(Error::InvalidAddress);
        }
        unsafe {
            // Guest memory can't strictly be modeled as a slice because it is
            // volatile.  Writing to it with what compiles down to a memcpy
            // won't hurt anything as long as we get the bounds checks right.
            let mut slice: &mut [u8] = &mut self.as_mut_slice()[offset..];
            Ok(slice.write(buf).map_err(Error::WriteToMemory)?)
        }
    }

    /// Reads to a slice from the memory region at the specified offset.
    /// Returns the number of bytes read.  The number of bytes read can
    /// be less than the length of the slice if there isn't enough room in the
    /// memory region.
    ///
    /// # Examples
    /// * Read a slice of size 16 at offset 256.
    ///
    /// ```
    /// #   use sys_util::MemoryMapping;
    /// #   let mut mem_map = MemoryMapping::new(1024).unwrap();
    ///     let buf = &mut [0u8; 16];
    ///     let res = mem_map.read_slice(buf, 256);
    ///     assert!(res.is_ok());
    ///     assert_eq!(res.unwrap(), 16);
    /// ```
    pub fn read_slice(&self, mut buf: &mut [u8], offset: usize) -> Result<usize> {
        if offset >= self.size {
            return Err(Error::InvalidAddress);
        }
        unsafe {
            // Guest memory can't strictly be modeled as a slice because it is
            // volatile.  Writing to it with what compiles down to a memcpy
            // won't hurt anything as long as we get the bounds checks right.
            let slice: &[u8] = &self.as_slice()[offset..];
            Ok(buf.write(slice).map_err(Error::ReadFromMemory)?)
        }
    }

    /// Writes an object to the memory region at the specified offset.
    /// Returns Ok(()) if the object fits, or Err if it extends past the end.
    ///
    /// # Examples
    /// * Write a u64 at offset 16.
    ///
    /// ```
    /// #   use sys_util::MemoryMapping;
    /// #   let mut mem_map = MemoryMapping::new(1024).unwrap();
    ///     let res = mem_map.write_obj(55u64, 16);
    ///     assert!(res.is_ok());
    /// ```
    pub fn write_obj<T: DataInit>(&self, val: T, offset: usize) -> Result<()> {
        unsafe {
            // Guest memory can't strictly be modeled as a slice because it is
            // volatile.  Writing to it with what compiles down to a memcpy
            // won't hurt anything as long as we get the bounds checks right.
            if offset + std::mem::size_of::<T>() > self.size {
                return Err(Error::InvalidAddress);
            }
            std::ptr::write_volatile(&mut self.as_mut_slice()[offset..] as *mut _ as *mut T, val);
            Ok(())
        }
    }

    /// Reads on object from the memory region at the given offset.
    /// Reading from a volatile area isn't strictly safe as it could change
    /// mid-read.  However, as long as the type T is plain old data and can
    /// handle random initialization, everything will be OK.
    ///
    /// # Examples
    /// * Read a u64 written to offset 32.
    ///
    /// ```
    /// #   use sys_util::MemoryMapping;
    /// #   let mut mem_map = MemoryMapping::new(1024).unwrap();
    ///     let res = mem_map.write_obj(55u64, 32);
    ///     assert!(res.is_ok());
    ///     let num: u64 = mem_map.read_obj(32).unwrap();
    ///     assert_eq!(55, num);
    /// ```
    pub fn read_obj<T: DataInit>(&self, offset: usize) -> Result<T> {
        if offset + std::mem::size_of::<T>() > self.size {
            return Err(Error::InvalidAddress);
        }
        unsafe {
            // This is safe because by definition Copy types can have their bits
            // set arbitrarily and still be valid.
            Ok(std::ptr::read_volatile(
                &self.as_slice()[offset..] as *const _ as *const T,
            ))
        }
    }

    /// Reads data from a readable object like a File and writes it to guest memory.
    ///
    /// # Arguments
    /// * `mem_offset` - Begin writing memory at this offset.
    /// * `src` - Read from `src` to memory.
    /// * `count` - Read `count` bytes from `src` to memory.
    ///
    /// # Examples
    ///
    /// * Read bytes from /dev/urandom
    ///
    /// ```
    /// # use sys_util::MemoryMapping;
    /// # use std::fs::File;
    /// # use std::path::Path;
    /// # fn test_read_random() -> Result<u32, ()> {
    /// #     let mut mem_map = MemoryMapping::new(1024).unwrap();
    ///       let mut file = File::open(Path::new("/dev/urandom")).map_err(|_| ())?;
    ///       mem_map.read_to_memory(32, &mut file, 128).map_err(|_| ())?;
    ///       let rand_val: u32 =  mem_map.read_obj(40).map_err(|_| ())?;
    /// #     Ok(rand_val)
    /// # }
    /// ```
    pub fn read_to_memory<F>(&self, mem_offset: usize, src: &mut F, count: usize) -> Result<()>
    where
        F: Read,
    {
        let mem_end = mem_offset + count;
        if mem_end > self.size() {
            return Err(Error::InvalidRange(mem_offset, count));
        }
        unsafe {
            // It is safe to overwrite the volatile memory.  Acessing the guest
            // memory as a mutable slice is OK because nothing assumes another
            // thread won't change what is loaded.
            let dst = &mut self.as_mut_slice()[mem_offset..mem_end];
            src.read_exact(dst).map_err(Error::ReadFromSource)?;
        }
        Ok(())
    }

    /// Writes data from memory to a writable object.
    ///
    /// # Arguments
    /// * `mem_offset` - Begin reading memory from this offset.
    /// * `dst` - Write from memory to `dst`.
    /// * `count` - Read `count` bytes from memory to `src`.
    ///
    /// # Examples
    ///
    /// * Write 128 bytes to /dev/null
    ///
    /// ```
    /// # use sys_util::MemoryMapping;
    /// # use std::fs::File;
    /// # use std::path::Path;
    /// # fn test_write_null() -> Result<(), ()> {
    /// #     let mut mem_map = MemoryMapping::new(1024).unwrap();
    ///       let mut file = File::open(Path::new("/dev/null")).map_err(|_| ())?;
    ///       mem_map.write_from_memory(32, &mut file, 128).map_err(|_| ())?;
    /// #     Ok(())
    /// # }
    /// ```
    pub fn write_from_memory<F>(&self, mem_offset: usize, dst: &mut F, count: usize) -> Result<()>
    where
        F: Write,
    {
        let mem_end = match mem_offset.checked_add(count) {
            None => return Err(Error::InvalidRange(mem_offset, count)),
            Some(m) => m,
        };
        if mem_end > self.size() {
            return Err(Error::InvalidRange(mem_offset, count));
        }
        unsafe {
            // It is safe to read from volatile memory.  Acessing the guest
            // memory as a slice is OK because nothing assumes another thread
            // won't change what is loaded.
            let src = &self.as_mut_slice()[mem_offset..mem_end];
            dst.write_all(src).map_err(Error::ReadFromSource)?;
        }
        Ok(())
    }

    unsafe fn as_slice(&self) -> &[u8] {
        // This is safe because we mapped the area at addr ourselves, so this slice will not
        // overflow. However, it is possible to alias.
        std::slice::from_raw_parts(self.addr, self.size)
    }

    unsafe fn as_mut_slice(&self) -> &mut [u8] {
        // This is safe because we mapped the area at addr ourselves, so this slice will not
        // overflow. However, it is possible to alias.
        std::slice::from_raw_parts_mut(self.addr, self.size)
    }
}

impl VolatileMemory for MemoryMapping {
    fn get_slice(&self, offset: usize, count: usize) -> VolatileMemoryResult<VolatileSlice> {
        let mem_end = calc_offset(offset, count)?;
        if mem_end > self.size {
            return Err(VolatileMemoryError::OutOfBounds { addr: mem_end });
        }

        // Safe because we checked that offset + count was within our range and we only ever hand
        // out volatile accessors.
        Ok(unsafe { VolatileSlice::new((self.addr as usize + offset) as *mut _, count) })
    }
}

impl Drop for MemoryMapping {
    fn drop(&mut self) {
        // This is safe because we mmap the area at addr ourselves, and nobody
        // else is holding a reference to it.
        unsafe {
            libc::munmap(self.addr as *mut libc::c_void, self.size);
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use data_model::{VolatileMemory, VolatileMemoryError};

    #[test]
    fn basic_map() {
        let m = MemoryMapping::new(1024).unwrap();
        assert_eq!(1024, m.size());
    }

    #[test]
    fn test_write_past_end() {
        let m = MemoryMapping::new(5).unwrap();
        let res = m.write_slice(&[1, 2, 3, 4, 5, 6], 0);
        assert!(res.is_ok());
        assert_eq!(res.unwrap(), 5);
    }

    #[test]
    fn slice_size() {
        let m = MemoryMapping::new(5).unwrap();
        let s = m.get_slice(2, 3).unwrap();
        assert_eq!(s.size(), 3);
    }

    #[test]
    fn slice_addr() {
        let m = MemoryMapping::new(5).unwrap();
        let s = m.get_slice(2, 3).unwrap();
        assert_eq!(s.as_ptr(), unsafe { m.as_ptr().offset(2) });
    }

    #[test]
    fn slice_store() {
        let m = MemoryMapping::new(5).unwrap();
        let r = m.get_ref(2).unwrap();
        r.store(9u16);
        assert_eq!(m.read_obj::<u16>(2).unwrap(), 9);
    }

    #[test]
    fn slice_overflow_error() {
        use std::usize;
        let m = MemoryMapping::new(5).unwrap();
        let res = m.get_slice(usize::MAX, 3).unwrap_err();
        assert_eq!(
            res,
            VolatileMemoryError::Overflow {
                base: usize::MAX,
                offset: 3,
            }
        );
    }
    #[test]
    fn slice_oob_error() {
        let m = MemoryMapping::new(5).unwrap();
        let res = m.get_slice(3, 3).unwrap_err();
        assert_eq!(res, VolatileMemoryError::OutOfBounds { addr: 6 });
    }
}
<|endoftext|>


<commit_before>//! Write benchmark information to the output repository

use errors::{Result, ResultExt};

use std::fs::{self, read_dir, File, OpenOptions};
use std::io::{BufRead, BufReader, Read, Write};
use std::path::PathBuf;
use std::process::Command;
use std::str;
use std::collections::HashSet;

use serde_json;
use collector::CommitData;
use rust_sysroot::git::Commit as GitCommit;
use execute::Benchmark;

pub struct Repo {
    path: PathBuf,
    use_remote: bool,
    retries: Vec<String>,
}

impl Repo {
    fn git(&self, args: &[&str]) -> Result<()> {
        let mut command = Command::new("git");
        command.current_dir(&self.path);
        info!("git {:?}", args);
        command.args(args);
        let status = command
            .status()
            .chain_err(|| format!("could not spawn git {:?}", args))?;
        if !status.success() {
            bail!(
                "command `git {:?}` failed in `{}`",
                args,
                self.path.display()
            );
        }
        Ok(())
    }

    pub fn open(path: PathBuf, use_remote: bool) -> Result<Self> {
        let mut result = Repo {
            path: path,
            use_remote,
            retries: vec![],
        };

        if !result.retries_file().exists() {
            // try not to nuke random repositories.
            bail!("`{}` file not present", result.retries_file().display());
        }

        if result.use_remote {
            result.git(&["fetch"])?;
            result.git(&["reset", "--hard", "@{upstream}"])?;
        }

        fs::create_dir_all(result.times()).chain_err(|| "can't create `times/`")?;
        result.load_retries()?;

        Ok(result)
    }

    pub fn success(&self, data: &CommitData) -> Result<()> {
        self.add_commit_data(data)?;
        self.commit_and_push(&format!("{} - success", data.commit.sha))?;
        Ok(())
    }

    pub fn find_missing_commits<'a>(
        &self,
        commits: &'a [GitCommit],
        benchmarks: &[Benchmark],
        triple: &str,
    ) -> Result<Vec<&'a GitCommit>> {
        let mut have = HashSet::new();
        let path = self.times();
        for entry in read_dir(path)? {
            let entry = entry?;
            let filename = entry.file_name().to_string_lossy().to_string();
            let sha =
                &filename[filename.find("00:00").unwrap() + 6..filename.find("-x86").unwrap()];
            have.insert(sha.to_string());
        }

        if let Ok(file) = File::open(self.broken_commits_file()) {
            let file = BufReader::new(file);
            for line in file.lines() {
                let line = line?;
                let sha = &line[..line.find(":").unwrap()];
                have.insert(sha.to_string());
            }
        }

        let missing = commits
            .iter()
            .filter(|c| {
                !have.contains(&c.sha) || {
                    self.load_commit_data(c, triple)
                        .ok()
                        .map(|data| {
                            benchmarks
                                .iter()
                                .any(|b| data.benchmarks.keys().find(|k| **k == b.name).is_none())
                        })
                        .unwrap_or(true)
                }
            })
            .collect::<Vec<_>>();

        Ok(missing)
    }

    fn commit_and_push(&self, message: &str) -> Result<()> {
        self.write_retries()?;
        self.git(&["add", "retries", "times"])?;
        self.git(&["commit", "-m", message])?;
        if self.use_remote {
            self.git(&["push"])?;
        }
        Ok(())
    }

    pub fn load_commit_data(&self, commit: &GitCommit, triple: &str) -> Result<CommitData> {
        let filepath = self.times().join(format!(
            "{}-{}-{}.json",
            commit.date.to_rfc3339(),
            commit.sha,
            triple
        ));
        info!("loading file {}", filepath.display());
        let mut file = File::open(&filepath)?;
        let mut contents = String::new();
        file.read_to_string(&mut contents)?;
        let data = serde_json::from_str(&contents)?;
        Ok(data)
    }

    pub fn add_commit_data(&self, data: &CommitData) -> Result<()> {
        let commit = &data.commit;
        let filepath = self.times().join(format!(
            "{}-{}-{}.json",
            commit.date,
            commit.sha,
            data.triple
        ));
        info!("creating file {}", filepath.display());
        let mut file = File::create(&filepath)?;
        serde_json::to_writer(&mut file, &data)?;
        Ok(())
    }

    fn load_retries(&mut self) -> Result<()> {
        let mut retries = OpenOptions::new()
            .read(true)
            .write(true)
            .create(true)
            .open(self.retries_file())
            .chain_err(|| {
                format!("can't create `{}`", self.retries_file().display())
            })?;
        let mut retries_s = String::new();
        retries.read_to_string(&mut retries_s)?;
        self.retries = retries_s
            .split('\n')
            .map(|line| line.trim())
            .filter(|line| !line.is_empty())
            .map(|line| if line.len() == 40 {
                Ok(line.to_owned())
            } else {
                bail!("bad retry hash `{}`", line)
            })
            .collect::<Result<_>>()?;
        info!("loaded retries: {:?}", self.retries);
        Ok(())
    }

    fn write_retries(&self) -> Result<()> {
        info!("writing retries");
        let mut retries = OpenOptions::new()
            .write(true)
            .truncate(true)
            .open(self.retries_file())
            .chain_err(|| "can't create `retries`")?;
        for retry in self.retries.iter() {
            writeln!(retries, "{}", retry)?;
        }
        Ok(())
    }

    pub fn next_retry(&mut self) -> Option<String> {
        if self.retries.len() == 0 {
            None
        } else {
            Some(self.retries.remove(0))
        }
    }

    fn broken_commits_file(&self) -> PathBuf {
        self.path.join("broken-commits-log")
    }

    fn retries_file(&self) -> PathBuf {
        self.path.join("retries")
    }

    pub fn times(&self) -> PathBuf {
        self.path.join("times")
    }
}
<commit_msg>Make sure we don't try to commit when nothing changed.<commit_after>//! Write benchmark information to the output repository

use errors::{Result, ResultExt};

use std::fs::{self, read_dir, File, OpenOptions};
use std::io::{BufRead, BufReader, Read, Write};
use std::path::PathBuf;
use std::process::Command;
use std::str;
use std::collections::HashSet;

use serde_json;
use collector::CommitData;
use rust_sysroot::git::Commit as GitCommit;
use execute::Benchmark;

pub struct Repo {
    path: PathBuf,
    use_remote: bool,
    retries: Vec<String>,
}

impl Repo {
    fn git(&self, args: &[&str]) -> Result<()> {
        let mut command = Command::new("git");
        command.current_dir(&self.path);
        info!("git {:?}", args);
        command.args(args);
        let status = command
            .status()
            .chain_err(|| format!("could not spawn git {:?}", args))?;
        if !status.success() {
            bail!(
                "command `git {:?}` failed in `{}`",
                args,
                self.path.display()
            );
        }
        Ok(())
    }

    pub fn open(path: PathBuf, use_remote: bool) -> Result<Self> {
        let mut result = Repo {
            path: path,
            use_remote,
            retries: vec![],
        };

        if !result.retries_file().exists() {
            // try not to nuke random repositories.
            bail!("`{}` file not present", result.retries_file().display());
        }

        if result.use_remote {
            result.git(&["fetch"])?;
            result.git(&["reset", "--hard", "@{upstream}"])?;
        }

        fs::create_dir_all(result.times()).chain_err(|| "can't create `times/`")?;
        result.load_retries()?;

        Ok(result)
    }

    pub fn success(&self, data: &CommitData) -> Result<()> {
        self.add_commit_data(data)?;
        self.commit_and_push(&format!("{} - success", data.commit.sha))?;
        Ok(())
    }

    pub fn find_missing_commits<'a>(
        &self,
        commits: &'a [GitCommit],
        benchmarks: &[Benchmark],
        triple: &str,
    ) -> Result<Vec<&'a GitCommit>> {
        let mut have = HashSet::new();
        let path = self.times();
        for entry in read_dir(path)? {
            let entry = entry?;
            let filename = entry.file_name().to_string_lossy().to_string();
            let sha =
                &filename[filename.find("00:00").unwrap() + 6..filename.find("-x86").unwrap()];
            have.insert(sha.to_string());
        }

        if let Ok(file) = File::open(self.broken_commits_file()) {
            let file = BufReader::new(file);
            for line in file.lines() {
                let line = line?;
                let sha = &line[..line.find(":").unwrap()];
                have.insert(sha.to_string());
            }
        }

        let missing = commits
            .iter()
            .filter(|c| {
                !have.contains(&c.sha) || {
                    self.load_commit_data(c, triple)
                        .ok()
                        .map(|data| {
                            benchmarks
                                .iter()
                                .any(|b| data.benchmarks.keys().find(|k| **k == b.name).is_none())
                        })
                        .unwrap_or(true)
                }
            })
            .collect::<Vec<_>>();

        Ok(missing)
    }

    fn commit_and_push(&self, message: &str) -> Result<()> {
        self.write_retries()?;
        self.git(&["add", "retries", "times"])?;
        if let Ok(_) = self.git(&["diff-index", "--quiet", "--cached", "HEAD"]) {
            self.git(&["commit", "-m", message])?;
            if self.use_remote {
                self.git(&["push"])?;
            }
        } else {
            println!("nothing to commit...");
        }
        Ok(())
    }

    pub fn load_commit_data(&self, commit: &GitCommit, triple: &str) -> Result<CommitData> {
        let filepath = self.times().join(format!(
            "{}-{}-{}.json",
            commit.date.to_rfc3339(),
            commit.sha,
            triple
        ));
        info!("loading file {}", filepath.display());
        let mut file = File::open(&filepath)?;
        let mut contents = String::new();
        file.read_to_string(&mut contents)?;
        let data = serde_json::from_str(&contents)?;
        Ok(data)
    }

    pub fn add_commit_data(&self, data: &CommitData) -> Result<()> {
        let commit = &data.commit;
        let filepath = self.times().join(format!(
            "{}-{}-{}.json",
            commit.date,
            commit.sha,
            data.triple
        ));
        info!("creating file {}", filepath.display());
        let mut file = File::create(&filepath)?;
        serde_json::to_writer(&mut file, &data)?;
        Ok(())
    }

    fn load_retries(&mut self) -> Result<()> {
        let mut retries = OpenOptions::new()
            .read(true)
            .write(true)
            .create(true)
            .open(self.retries_file())
            .chain_err(|| {
                format!("can't create `{}`", self.retries_file().display())
            })?;
        let mut retries_s = String::new();
        retries.read_to_string(&mut retries_s)?;
        self.retries = retries_s
            .split('\n')
            .map(|line| line.trim())
            .filter(|line| !line.is_empty())
            .map(|line| if line.len() == 40 {
                Ok(line.to_owned())
            } else {
                bail!("bad retry hash `{}`", line)
            })
            .collect::<Result<_>>()?;
        info!("loaded retries: {:?}", self.retries);
        Ok(())
    }

    fn write_retries(&self) -> Result<()> {
        info!("writing retries");
        let mut retries = OpenOptions::new()
            .write(true)
            .truncate(true)
            .open(self.retries_file())
            .chain_err(|| "can't create `retries`")?;
        for retry in self.retries.iter() {
            writeln!(retries, "{}", retry)?;
        }
        Ok(())
    }

    pub fn next_retry(&mut self) -> Option<String> {
        if self.retries.len() == 0 {
            None
        } else {
            Some(self.retries.remove(0))
        }
    }

    fn broken_commits_file(&self) -> PathBuf {
        self.path.join("broken-commits-log")
    }

    fn retries_file(&self) -> PathBuf {
        self.path.join("retries")
    }

    pub fn times(&self) -> PathBuf {
        self.path.join("times")
    }
}
<|endoftext|>