<commit_before>// Copyright 2019 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.

use std::fmt::{self, Display};
use std::mem;
use std::result;
use std::slice;

use data_model::DataInit;
use vm_memory::{GuestAddress, GuestMemory};

#[derive(Debug)]
pub enum Error {
    /// There was too little guest memory to store the entire SMBIOS table.
    NotEnoughMemory,
    /// The SMBIOS table has too little address space to be stored.
    AddressOverflow,
    /// Failure while zeroing out the memory for the SMBIOS table.
    Clear,
    /// Failure to write SMBIOS entrypoint structure
    WriteSmbiosEp,
    /// Failure to write additional data to memory
    WriteData,
}

impl std::error::Error for Error {}

impl Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::Error::*;

        let description = match self {
            NotEnoughMemory => "There was too little guest memory to store the SMBIOS table",
            AddressOverflow => "The SMBIOS table has too little address space to be stored",
            Clear => "Failure while zeroing out the memory for the SMBIOS table",
            WriteSmbiosEp => "Failure to write SMBIOS entrypoint structure",
            WriteData => "Failure to write additional data to memory",
        };

        write!(f, "SMBIOS error: {}", description)
    }
}

pub type Result<T> = result::Result<T, Error>;

const SMBIOS_START: u64 = 0xf0000; // First possible location per the spec.

// Constants sourced from SMBIOS Spec 3.2.0.
const SM3_MAGIC_IDENT: &[u8; 5usize] = b"_SM3_";
const BIOS_INFORMATION: u8 = 0;
const SYSTEM_INFORMATION: u8 = 1;
const PCI_SUPPORTED: u64 = 1 << 7;
const IS_VIRTUAL_MACHINE: u8 = 1 << 4;

fn compute_checksum<T: Copy>(v: &T) -> u8 {
    // Safe because we are only reading the bytes within the size of the `T` reference `v`.
    let v_slice = unsafe { slice::from_raw_parts(v as *const T as *const u8, mem::size_of::<T>()) };
    let mut checksum: u8 = 0;
    for i in v_slice.iter() {
        checksum = checksum.wrapping_add(*i);
    }
    (!checksum).wrapping_add(1)
}

#[repr(packed)]
#[derive(Default, Copy)]
pub struct Smbios30Entrypoint {
    pub signature: [u8; 5usize],
    pub checksum: u8,
    pub length: u8,
    pub majorver: u8,
    pub minorver: u8,
    pub docrev: u8,
    pub revision: u8,
    pub reserved: u8,
    pub max_size: u32,
    pub physptr: u64,
}
unsafe impl data_model::DataInit for Smbios30Entrypoint {}

impl Clone for Smbios30Entrypoint {
    fn clone(&self) -> Self {
        *self
    }
}

#[repr(packed)]
#[derive(Default, Copy)]
pub struct SmbiosBiosInfo {
    pub typ: u8,
    pub length: u8,
    pub handle: u16,
    pub vendor: u8,
    pub version: u8,
    pub start_addr: u16,
    pub release_date: u8,
    pub rom_size: u8,
    pub characteristics: u64,
    pub characteristics_ext1: u8,
    pub characteristics_ext2: u8,
}

impl Clone for SmbiosBiosInfo {
    fn clone(&self) -> Self {
        *self
    }
}

unsafe impl data_model::DataInit for SmbiosBiosInfo {}

#[repr(packed)]
#[derive(Default, Copy)]
pub struct SmbiosSysInfo {
    pub typ: u8,
    pub length: u8,
    pub handle: u16,
    pub manufacturer: u8,
    pub product_name: u8,
    pub version: u8,
    pub serial_number: u8,
    pub uuid: [u8; 16usize],
    pub wake_up_type: u8,
    pub sku: u8,
    pub family: u8,
}

impl Clone for SmbiosSysInfo {
    fn clone(&self) -> Self {
        *self
    }
}

unsafe impl data_model::DataInit for SmbiosSysInfo {}

fn write_and_incr<T: DataInit>(
    mem: &GuestMemory,
    val: T,
    mut curptr: GuestAddress,
) -> Result<GuestAddress> {
    mem.write_obj_at_addr(val, curptr)
        .map_err(|_| Error::WriteData)?;
    curptr = curptr
        .checked_add(mem::size_of::<T>() as u64)
        .ok_or(Error::NotEnoughMemory)?;
    Ok(curptr)
}

fn write_string(mem: &GuestMemory, val: &str, mut curptr: GuestAddress) -> Result<GuestAddress> {
    for c in val.as_bytes().iter() {
        curptr = write_and_incr(mem, *c, curptr)?;
    }
    curptr = write_and_incr(mem, 0_u8, curptr)?;
    Ok(curptr)
}

pub fn setup_smbios(mem: &GuestMemory) -> Result<()> {
    let physptr = GuestAddress(SMBIOS_START)
        .checked_add(mem::size_of::<Smbios30Entrypoint>() as u64)
        .ok_or(Error::NotEnoughMemory)?;
    let mut curptr = physptr;
    let mut handle = 0;

    {
        handle += 1;
        let smbios_biosinfo = SmbiosBiosInfo {
            typ: BIOS_INFORMATION,
            length: mem::size_of::<SmbiosBiosInfo>() as u8,
            handle,
            vendor: 1,  // First string written in this section
            version: 2, // Second string written in this section
            characteristics: PCI_SUPPORTED,
            characteristics_ext2: IS_VIRTUAL_MACHINE,
            ..Default::default()
        };
        curptr = write_and_incr(mem, smbios_biosinfo, curptr)?;
        curptr = write_string(mem, "crosvm", curptr)?;
        curptr = write_string(mem, "0", curptr)?;
        curptr = write_and_incr(mem, 0_u8, curptr)?;
    }

    {
        handle += 1;
        let smbios_sysinfo = SmbiosSysInfo {
            typ: SYSTEM_INFORMATION,
            length: mem::size_of::<SmbiosSysInfo>() as u8,
            handle,
            manufacturer: 1, // First string written in this section
            product_name: 2, // Second string written in this section
            ..Default::default()
        };
        curptr = write_and_incr(mem, smbios_sysinfo, curptr)?;
        curptr = write_string(mem, "ChromiumOS", curptr)?;
        curptr = write_string(mem, "crosvm", curptr)?;
        curptr = write_and_incr(mem, 0u8, curptr)?;
    }

    {
        let mut smbios_ep = Smbios30Entrypoint::default();
        smbios_ep.signature = *SM3_MAGIC_IDENT;
        smbios_ep.length = mem::size_of::<Smbios30Entrypoint>() as u8;
        // SMBIOS rev 3.2.0
        smbios_ep.majorver = 0x03;
        smbios_ep.minorver = 0x02;
        smbios_ep.docrev = 0x00;
        smbios_ep.revision = 0x01; // SMBIOS 3.0
        smbios_ep.max_size = curptr.offset_from(physptr) as u32;
        smbios_ep.physptr = physptr.offset();
        smbios_ep.checksum = compute_checksum(&smbios_ep);
        mem.write_obj_at_addr(smbios_ep, GuestAddress(SMBIOS_START))
            .map_err(|_| Error::WriteSmbiosEp)?;
    }

    Ok(())
}

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

    #[test]
    fn struct_size() {
        assert_eq!(
            mem::size_of::<Smbios30Entrypoint>(),
            0x18usize,
            concat!("Size of: ", stringify!(Smbios30Entrypoint))
        );
        assert_eq!(
            mem::size_of::<SmbiosBiosInfo>(),
            0x14usize,
            concat!("Size of: ", stringify!(SmbiosBiosInfo))
        );
        assert_eq!(
            mem::size_of::<SmbiosSysInfo>(),
            0x1busize,
            concat!("Size of: ", stringify!(SmbiosSysInfo))
        );
    }

    #[test]
    fn entrypoint_checksum() {
        let mem = GuestMemory::new(&[(GuestAddress(SMBIOS_START), 4096)]).unwrap();

        setup_smbios(&mem).unwrap();

        let smbios_ep: Smbios30Entrypoint =
            mem.read_obj_from_addr(GuestAddress(SMBIOS_START)).unwrap();

        assert_eq!(compute_checksum(&smbios_ep), 0);
    }
}
<commit_msg>x86_64:Add smbios end entry<commit_after>// Copyright 2019 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.

use std::fmt::{self, Display};
use std::mem;
use std::result;
use std::slice;

use data_model::DataInit;
use vm_memory::{GuestAddress, GuestMemory};

#[derive(Debug)]
pub enum Error {
    /// There was too little guest memory to store the entire SMBIOS table.
    NotEnoughMemory,
    /// The SMBIOS table has too little address space to be stored.
    AddressOverflow,
    /// Failure while zeroing out the memory for the SMBIOS table.
    Clear,
    /// Failure to write SMBIOS entrypoint structure
    WriteSmbiosEp,
    /// Failure to write additional data to memory
    WriteData,
}

impl std::error::Error for Error {}

impl Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::Error::*;

        let description = match self {
            NotEnoughMemory => "There was too little guest memory to store the SMBIOS table",
            AddressOverflow => "The SMBIOS table has too little address space to be stored",
            Clear => "Failure while zeroing out the memory for the SMBIOS table",
            WriteSmbiosEp => "Failure to write SMBIOS entrypoint structure",
            WriteData => "Failure to write additional data to memory",
        };

        write!(f, "SMBIOS error: {}", description)
    }
}

pub type Result<T> = result::Result<T, Error>;

const SMBIOS_START: u64 = 0xf0000; // First possible location per the spec.

// Constants sourced from SMBIOS Spec 3.2.0.
const SM3_MAGIC_IDENT: &[u8; 5usize] = b"_SM3_";
const BIOS_INFORMATION: u8 = 0;
const SYSTEM_INFORMATION: u8 = 1;
const END_OF_TABLE: u8 = 127;
const PCI_SUPPORTED: u64 = 1 << 7;
const IS_VIRTUAL_MACHINE: u8 = 1 << 4;

fn compute_checksum<T: Copy>(v: &T) -> u8 {
    // Safe because we are only reading the bytes within the size of the `T` reference `v`.
    let v_slice = unsafe { slice::from_raw_parts(v as *const T as *const u8, mem::size_of::<T>()) };
    let mut checksum: u8 = 0;
    for i in v_slice.iter() {
        checksum = checksum.wrapping_add(*i);
    }
    (!checksum).wrapping_add(1)
}

#[repr(packed)]
#[derive(Default, Copy)]
pub struct Smbios30Entrypoint {
    pub signature: [u8; 5usize],
    pub checksum: u8,
    pub length: u8,
    pub majorver: u8,
    pub minorver: u8,
    pub docrev: u8,
    pub revision: u8,
    pub reserved: u8,
    pub max_size: u32,
    pub physptr: u64,
}
unsafe impl data_model::DataInit for Smbios30Entrypoint {}

impl Clone for Smbios30Entrypoint {
    fn clone(&self) -> Self {
        *self
    }
}

#[repr(packed)]
#[derive(Default, Copy)]
pub struct SmbiosBiosInfo {
    pub typ: u8,
    pub length: u8,
    pub handle: u16,
    pub vendor: u8,
    pub version: u8,
    pub start_addr: u16,
    pub release_date: u8,
    pub rom_size: u8,
    pub characteristics: u64,
    pub characteristics_ext1: u8,
    pub characteristics_ext2: u8,
}

impl Clone for SmbiosBiosInfo {
    fn clone(&self) -> Self {
        *self
    }
}

unsafe impl data_model::DataInit for SmbiosBiosInfo {}

#[repr(packed)]
#[derive(Default, Copy)]
pub struct SmbiosSysInfo {
    pub typ: u8,
    pub length: u8,
    pub handle: u16,
    pub manufacturer: u8,
    pub product_name: u8,
    pub version: u8,
    pub serial_number: u8,
    pub uuid: [u8; 16usize],
    pub wake_up_type: u8,
    pub sku: u8,
    pub family: u8,
}

impl Clone for SmbiosSysInfo {
    fn clone(&self) -> Self {
        *self
    }
}

unsafe impl data_model::DataInit for SmbiosSysInfo {}

fn write_and_incr<T: DataInit>(
    mem: &GuestMemory,
    val: T,
    mut curptr: GuestAddress,
) -> Result<GuestAddress> {
    mem.write_obj_at_addr(val, curptr)
        .map_err(|_| Error::WriteData)?;
    curptr = curptr
        .checked_add(mem::size_of::<T>() as u64)
        .ok_or(Error::NotEnoughMemory)?;
    Ok(curptr)
}

fn write_string(mem: &GuestMemory, val: &str, mut curptr: GuestAddress) -> Result<GuestAddress> {
    for c in val.as_bytes().iter() {
        curptr = write_and_incr(mem, *c, curptr)?;
    }
    curptr = write_and_incr(mem, 0_u8, curptr)?;
    Ok(curptr)
}

pub fn setup_smbios(mem: &GuestMemory) -> Result<()> {
    let physptr = GuestAddress(SMBIOS_START)
        .checked_add(mem::size_of::<Smbios30Entrypoint>() as u64)
        .ok_or(Error::NotEnoughMemory)?;
    let mut curptr = physptr;
    let mut handle = 0;

    {
        handle += 1;
        let smbios_biosinfo = SmbiosBiosInfo {
            typ: BIOS_INFORMATION,
            length: mem::size_of::<SmbiosBiosInfo>() as u8,
            handle,
            vendor: 1,  // First string written in this section
            version: 2, // Second string written in this section
            characteristics: PCI_SUPPORTED,
            characteristics_ext2: IS_VIRTUAL_MACHINE,
            ..Default::default()
        };
        curptr = write_and_incr(mem, smbios_biosinfo, curptr)?;
        curptr = write_string(mem, "crosvm", curptr)?;
        curptr = write_string(mem, "0", curptr)?;
        curptr = write_and_incr(mem, 0_u8, curptr)?;
    }

    {
        handle += 1;
        let smbios_sysinfo = SmbiosSysInfo {
            typ: SYSTEM_INFORMATION,
            length: mem::size_of::<SmbiosSysInfo>() as u8,
            handle,
            manufacturer: 1, // First string written in this section
            product_name: 2, // Second string written in this section
            ..Default::default()
        };
        curptr = write_and_incr(mem, smbios_sysinfo, curptr)?;
        curptr = write_string(mem, "ChromiumOS", curptr)?;
        curptr = write_string(mem, "crosvm", curptr)?;
        curptr = write_and_incr(mem, 0u8, curptr)?;
    }

    {
        handle += 1;
        let smbios_sysinfo = SmbiosSysInfo {
            typ: END_OF_TABLE,
            length: mem::size_of::<SmbiosSysInfo>() as u8,
            handle,
            ..Default::default()
        };
        curptr = write_and_incr(mem, smbios_sysinfo, curptr)?;
        curptr = write_and_incr(mem, 0_u8, curptr)?;
    }

    {
        let mut smbios_ep = Smbios30Entrypoint::default();
        smbios_ep.signature = *SM3_MAGIC_IDENT;
        smbios_ep.length = mem::size_of::<Smbios30Entrypoint>() as u8;
        // SMBIOS rev 3.2.0
        smbios_ep.majorver = 0x03;
        smbios_ep.minorver = 0x02;
        smbios_ep.docrev = 0x00;
        smbios_ep.revision = 0x01; // SMBIOS 3.0
        smbios_ep.max_size = curptr.offset_from(physptr) as u32;
        smbios_ep.physptr = physptr.offset();
        smbios_ep.checksum = compute_checksum(&smbios_ep);
        mem.write_obj_at_addr(smbios_ep, GuestAddress(SMBIOS_START))
            .map_err(|_| Error::WriteSmbiosEp)?;
    }

    Ok(())
}

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

    #[test]
    fn struct_size() {
        assert_eq!(
            mem::size_of::<Smbios30Entrypoint>(),
            0x18usize,
            concat!("Size of: ", stringify!(Smbios30Entrypoint))
        );
        assert_eq!(
            mem::size_of::<SmbiosBiosInfo>(),
            0x14usize,
            concat!("Size of: ", stringify!(SmbiosBiosInfo))
        );
        assert_eq!(
            mem::size_of::<SmbiosSysInfo>(),
            0x1busize,
            concat!("Size of: ", stringify!(SmbiosSysInfo))
        );
    }

    #[test]
    fn entrypoint_checksum() {
        let mem = GuestMemory::new(&[(GuestAddress(SMBIOS_START), 4096)]).unwrap();

        setup_smbios(&mem).unwrap();

        let smbios_ep: Smbios30Entrypoint =
            mem.read_obj_from_addr(GuestAddress(SMBIOS_START)).unwrap();

        assert_eq!(compute_checksum(&smbios_ep), 0);
    }
}
<|endoftext|>


<commit_before>use std::{
    io::{stdin, stdout, BufRead, Write},
    thread::sleep,
    time::Duration,
};

use isahc::{
    auth::{Authentication, Credentials},
    config::Configurable,
    http::StatusCode,
    HttpClient, ReadResponseExt, Request,
};
use semver::{Identifier, Version};
use serde::Deserialize;
use serde_json::json;

use crate::{cargo::Package, cmd, util::ask_yes_no, GithubConfig, Metadata, Result};

const GITHUB_API_RUMA: &str = "https://api.github.com/repos/ruma/ruma";

/// Task to create a new release of the given crate.
#[derive(Debug)]
pub struct ReleaseTask {
    /// The metadata of the cargo workspace.
    metadata: Metadata,

    /// The crate to release.
    package: Package,

    /// The new version of the crate.
    version: Version,

    /// The http client to use for requests.
    http_client: HttpClient,

    /// The github configuration required to publish a release.
    config: GithubConfig,
}

impl ReleaseTask {
    /// Create a new `ReleaseTask` with the given `name` and `version`.
    pub(crate) fn new(name: String, version: Version) -> Result<Self> {
        let metadata = Metadata::load()?;

        let package = metadata
            .packages
            .clone()
            .into_iter()
            .find(|p| p.name == name)
            .ok_or(format!("Package {} not found in cargo metadata", name))?;

        let config = crate::Config::load()?.github;

        let http_client = HttpClient::new()?;

        Ok(Self { metadata, package, version, http_client, config })
    }

    /// Run the task to effectively create a release.
    pub(crate) fn run(&mut self) -> Result<()> {
        let title = &self.title();
        let prerelease = self.version.is_prerelease();
        let publish_only = self.package.name == "ruma-identifiers-validation";

        println!(
            "Starting {} for {}…",
            match prerelease {
                true => "pre-release",
                false => "release",
            },
            title
        );

        if self.is_released()? {
            return Err("This crate version is already released".into());
        }

        let remote = Self::git_remote()?;

        println!("Checking status of git repository…");
        if !cmd!("git status -s -uno").read()?.is_empty()
            && !ask_yes_no("This git repository contains uncommitted changes. Continue?")?
        {
            return Ok(());
        }

        if self.package.version != self.version
            && !self.package.version.is_next(&self.version)
            && !ask_yes_no(&format!(
                "Version {} should not follow version {}. Do you really want to continue?",
                self.version, self.package.version,
            ))?
        {
            return Ok(());
        }

        let mut macros = self.macros();

        if let Some(m) = macros.as_mut() {
            println!("Found macros crate {}.", m.name);

            m.update_version(&self.version)?;
            m.update_dependants(&self.metadata)?;

            println!("Resuming release of {}…", self.title());
        }

        self.package.update_version(&self.version)?;
        self.package.update_dependants(&self.metadata)?;

        let changes = &self.package.changes(!prerelease)?;

        self.commit()?;

        if let Some(m) = macros {
            let published = m.publish(&self.http_client)?;

            if published {
                // Crate was published, instead of publishing skipped (because release already
                // existed).
                println!("Waiting 10 seconds for the release to make it into the crates.io index…");
                sleep(Duration::from_secs(10));
            }
        }

        self.package.publish(&self.http_client)?;

        if publish_only {
            println!("Crate published successfully!");
            return Ok(());
        }

        let tag = &self.tag_name();

        println!("Creating git tag…");
        if cmd!("git tag -l {tag}").read()?.is_empty() {
            cmd!("git tag -s {tag} -m {title} -m {changes}").read()?;
        } else if !ask_yes_no("This tag already exists. Skip this step and continue?")? {
            return Ok(());
        }

        println!("Pushing tag to remote repository…");
        if cmd!("git ls-remote --tags {remote} {tag}").read()?.is_empty() {
            cmd!("git push {remote} {tag}").run()?;
        } else if !ask_yes_no("This tag has already been pushed. Skip this step and continue?")? {
            return Ok(());
        }

        if prerelease {
            println!("Pre-release created successfully!");
            return Ok(());
        }

        println!("Creating release on GitHub…");
        let request_body = &json!({
            "tag_name": tag,
            "name": title,
            "body": changes.trim_softbreaks(),
        })
        .to_string();

        self.release(request_body)?;

        println!("Release created successfully!");

        Ok(())
    }

    /// Get the associated `-macros` crate of the current crate, if any.
    fn macros(&self) -> Option<Package> {
        self.metadata
            .packages
            .clone()
            .into_iter()
            .find(|p| p.name == format!("{}-macros", self.package.name))
    }

    /// Get the title of this release.
    fn title(&self) -> String {
        format!("{} {}", self.package.name, self.version)
    }

    /// Get the tag name for this release.
    fn tag_name(&self) -> String {
        format!("{}-{}", self.package.name, self.version)
    }

    /// Load the GitHub config from the config file.
    fn git_remote() -> Result<String> {
        let branch = cmd!("git rev-parse --abbrev-ref HEAD").read()?;
        let remote = cmd!("git config branch.{branch}.remote").read()?;

        if remote.is_empty() {
            return Err("Could not get current git remote".into());
        }

        Ok(remote)
    }

    /// Commit and push all the changes in the git repository.
    fn commit(&self) -> Result<()> {
        let stdin = stdin();

        let instructions = "Ready to commit the changes. [continue/abort/diff]: ";
        print!("{}", instructions);
        stdout().flush()?;

        let mut handle = stdin.lock();

        let mut input = String::new();
        loop {
            let eof = handle.read_line(&mut input)? == 0;
            if eof {
                return Err("User aborted commit".into());
            }

            match input.trim().to_ascii_lowercase().as_str() {
                "c" | "continue" => {
                    break;
                }
                "a" | "abort" => {
                    return Err("User aborted commit".into());
                }
                "d" | "diff" => {
                    cmd!("git diff").run()?;
                }
                _ => {
                    println!("Unknown command.");
                }
            }
            print!("{}", instructions);
            stdout().flush()?;

            input.clear();
        }

        let message = format!("Release {}", self.title());

        println!("Creating commit…");
        cmd!("git commit -a -m {message}").read()?;

        println!("Pushing commit…");
        cmd!("git push").read()?;

        Ok(())
    }

    /// Check if the tag for the current version of the crate has been pushed on GitHub.
    fn is_released(&self) -> Result<bool> {
        let response = self.http_client.get(format!(
            "{}/releases/tags/{}",
            GITHUB_API_RUMA,
            self.tag_name()
        ))?;

        Ok(response.status() == StatusCode::OK)
    }

    /// Create the release on GitHub with the given `config` and `credentials`.
    fn release(&self, body: &str) -> Result<()> {
        let request = Request::post(format!("{}/releases", GITHUB_API_RUMA))
            .authentication(Authentication::basic())
            .credentials(Credentials::new(&self.config.user, &self.config.token))
            .header("Accept", "application/vnd.github.v3+json")
            .body(body)?;

        let mut response = self.http_client.send(request)?;

        if response.status() == StatusCode::CREATED {
            Ok(())
        } else {
            Err(format!("{}: {}", response.status(), response.text()?).into())
        }
    }
}

/// A tag from the `GET /repos/{owner}/{repo}/tags` endpoint of GitHub REST API.
#[derive(Debug, Deserialize)]
struct GithubTag {
    /// The name of the tag.
    name: String,
}

/// String manipulations for crate release.
trait StrExt {
    /// Remove soft line breaks as defined in CommonMark spec.
    fn trim_softbreaks(&self) -> String;
}

impl StrExt for str {
    fn trim_softbreaks(&self) -> String {
        let mut string = String::new();
        let mut s = self;

        while let Some(pos) = s.find('\n') {
            string.push_str(&s[..pos]);
            let pos_s = &s[pos..];

            if pos_s.starts_with("\n\n") {
                // Keep new paragraphs (multiple `\n`s).
                let next = pos_s.find(|c: char| c != '\n').unwrap_or(0);
                let (push, next_s) = pos_s.split_at(next);

                string.push_str(push);
                s = next_s;
            } else if s[..pos].ends_with("  ") || s[..pos].ends_with('\\') {
                // Keep hard line breaks (two spaces or a backslash before the line break).
                string.push('\n');
                s = &pos_s[1..];
            } else if let Some(p) = pos_s.find(|c: char| !c.is_ascii_whitespace()) {
                // Keep line break before list items (`\n` + whitespaces + `*` + whitespaces).
                // Remove line break and keep one space otherwise.
                let mut chars = pos_s.char_indices();
                let (_, char) = chars.find(|(i, _)| *i == p).unwrap();

                if char == '*' || char == '-' {
                    match chars.next() {
                        Some((_, next_char)) if next_char.is_ascii_whitespace() => {
                            string.push('\n');
                            s = &pos_s[1..];
                            continue;
                        }
                        _ => {}
                    }
                }

                string.push(' ');
                s = &pos_s[p..];
            }
        }

        string + s
    }
}

/// Extra Version increment methods for crate release.
trait VersionExt {
    /// Adds a pre-release label and number if there is none.
    fn add_pre_release(&mut self);

    /// Increments the pre-release number, if this is a pre-release.
    fn increment_pre_number(&mut self);

    /// Increments the pre-release label from `alpha` to `beta` if this is a pre-release and it is
    /// possible, otherwise does nothing.
    fn increment_pre_label(&mut self);

    /// If the given version can be the next after this one.
    ///
    /// This checks all the version bumps of the format MAJOR.MINOR.PATCH-PRE_LABEL.PRE_NUMBER, with
    /// PRE_LABEL = alpha or beta.
    fn is_next(&self, version: &Version) -> bool;
}

impl VersionExt for Version {
    fn add_pre_release(&mut self) {
        if !self.is_prerelease() {
            self.pre = vec![Identifier::AlphaNumeric("alpha".into()), Identifier::Numeric(1)];
        }
    }

    fn increment_pre_number(&mut self) {
        if self.is_prerelease() {
            if let Identifier::Numeric(n) = self.pre[1] {
                self.pre[1] = Identifier::Numeric(n + 1);
            }
        }
    }

    fn increment_pre_label(&mut self) {
        if self.is_prerelease() {
            match &self.pre[0] {
                Identifier::AlphaNumeric(n) if n == "alpha" => {
                    self.pre =
                        vec![Identifier::AlphaNumeric("beta".into()), Identifier::Numeric(1)];
                }
                _ => {}
            }
        }
    }

    fn is_next(&self, version: &Version) -> bool {
        let mut next = self.clone();

        if self.is_prerelease() {
            next.increment_pre_number();
            if next == *version {
                return true;
            }

            next.increment_pre_label();
            if next == *version {
                return true;
            }

            next.pre = vec![];
            if next == *version {
                return true;
            }
        } else {
            next.increment_patch();
            if next == *version {
                return true;
            }

            next.add_pre_release();
            if next == *version {
                return true;
            }

            next.increment_minor();
            if next == *version {
                return true;
            }

            next.add_pre_release();
            if next == *version {
                return true;
            }

            next.increment_major();
            if next == *version {
                return true;
            }

            next.add_pre_release();
            if next == *version {
                return true;
            }
        }

        false
    }
}
<commit_msg>xtask: Disallow releasing a macro crate without its parent crate<commit_after>use std::{
    io::{stdin, stdout, BufRead, Write},
    thread::sleep,
    time::Duration,
};

use isahc::{
    auth::{Authentication, Credentials},
    config::Configurable,
    http::StatusCode,
    HttpClient, ReadResponseExt, Request,
};
use semver::{Identifier, Version};
use serde::Deserialize;
use serde_json::json;

use crate::{cargo::Package, cmd, util::ask_yes_no, GithubConfig, Metadata, Result};

const GITHUB_API_RUMA: &str = "https://api.github.com/repos/ruma/ruma";

/// Task to create a new release of the given crate.
#[derive(Debug)]
pub struct ReleaseTask {
    /// The metadata of the cargo workspace.
    metadata: Metadata,

    /// The crate to release.
    package: Package,

    /// The new version of the crate.
    version: Version,

    /// The http client to use for requests.
    http_client: HttpClient,

    /// The github configuration required to publish a release.
    config: GithubConfig,
}

impl ReleaseTask {
    /// Create a new `ReleaseTask` with the given `name` and `version`.
    pub(crate) fn new(name: String, version: Version) -> Result<Self> {
        let metadata = Metadata::load()?;

        let package = metadata
            .packages
            .clone()
            .into_iter()
            .find(|p| p.name == name)
            .ok_or(format!("Package {} not found in cargo metadata", name))?;

        let config = crate::Config::load()?.github;

        let http_client = HttpClient::new()?;

        Ok(Self { metadata, package, version, http_client, config })
    }

    /// Run the task to effectively create a release.
    pub(crate) fn run(&mut self) -> Result<()> {
        let title = &self.title();
        let prerelease = self.version.is_prerelease();
        let publish_only = self.package.name == "ruma-identifiers-validation";

        if let Some(name) = self.package.name.strip_suffix("-macros") {
            return Err(format!(
                "Macro crates are always released together with their parent crate.\n\
                 To release both {main_cr} and {macro_cr}, simply run\n\
                 \n\
                 cargo xtask release {main_cr}",
                main_cr = name,
                macro_cr = self.package.name,
            )
            .into());
        }

        println!(
            "Starting {} for {}…",
            match prerelease {
                true => "pre-release",
                false => "release",
            },
            title
        );

        if self.is_released()? {
            return Err("This crate version is already released".into());
        }

        let remote = Self::git_remote()?;

        println!("Checking status of git repository…");
        if !cmd!("git status -s -uno").read()?.is_empty()
            && !ask_yes_no("This git repository contains uncommitted changes. Continue?")?
        {
            return Ok(());
        }

        if self.package.version != self.version
            && !self.package.version.is_next(&self.version)
            && !ask_yes_no(&format!(
                "Version {} should not follow version {}. Do you really want to continue?",
                self.version, self.package.version,
            ))?
        {
            return Ok(());
        }

        let mut macros = self.macros();

        if let Some(m) = macros.as_mut() {
            println!("Found macros crate {}.", m.name);

            m.update_version(&self.version)?;
            m.update_dependants(&self.metadata)?;

            println!("Resuming release of {}…", self.title());
        }

        self.package.update_version(&self.version)?;
        self.package.update_dependants(&self.metadata)?;

        let changes = &self.package.changes(!prerelease)?;

        self.commit()?;

        if let Some(m) = macros {
            let published = m.publish(&self.http_client)?;

            if published {
                // Crate was published, instead of publishing skipped (because release already
                // existed).
                println!("Waiting 10 seconds for the release to make it into the crates.io index…");
                sleep(Duration::from_secs(10));
            }
        }

        self.package.publish(&self.http_client)?;

        if publish_only {
            println!("Crate published successfully!");
            return Ok(());
        }

        let tag = &self.tag_name();

        println!("Creating git tag…");
        if cmd!("git tag -l {tag}").read()?.is_empty() {
            cmd!("git tag -s {tag} -m {title} -m {changes}").read()?;
        } else if !ask_yes_no("This tag already exists. Skip this step and continue?")? {
            return Ok(());
        }

        println!("Pushing tag to remote repository…");
        if cmd!("git ls-remote --tags {remote} {tag}").read()?.is_empty() {
            cmd!("git push {remote} {tag}").run()?;
        } else if !ask_yes_no("This tag has already been pushed. Skip this step and continue?")? {
            return Ok(());
        }

        if prerelease {
            println!("Pre-release created successfully!");
            return Ok(());
        }

        println!("Creating release on GitHub…");
        let request_body = &json!({
            "tag_name": tag,
            "name": title,
            "body": changes.trim_softbreaks(),
        })
        .to_string();

        self.release(request_body)?;

        println!("Release created successfully!");

        Ok(())
    }

    /// Get the associated `-macros` crate of the current crate, if any.
    fn macros(&self) -> Option<Package> {
        self.metadata
            .packages
            .clone()
            .into_iter()
            .find(|p| p.name == format!("{}-macros", self.package.name))
    }

    /// Get the title of this release.
    fn title(&self) -> String {
        format!("{} {}", self.package.name, self.version)
    }

    /// Get the tag name for this release.
    fn tag_name(&self) -> String {
        format!("{}-{}", self.package.name, self.version)
    }

    /// Load the GitHub config from the config file.
    fn git_remote() -> Result<String> {
        let branch = cmd!("git rev-parse --abbrev-ref HEAD").read()?;
        let remote = cmd!("git config branch.{branch}.remote").read()?;

        if remote.is_empty() {
            return Err("Could not get current git remote".into());
        }

        Ok(remote)
    }

    /// Commit and push all the changes in the git repository.
    fn commit(&self) -> Result<()> {
        let stdin = stdin();

        let instructions = "Ready to commit the changes. [continue/abort/diff]: ";
        print!("{}", instructions);
        stdout().flush()?;

        let mut handle = stdin.lock();

        let mut input = String::new();
        loop {
            let eof = handle.read_line(&mut input)? == 0;
            if eof {
                return Err("User aborted commit".into());
            }

            match input.trim().to_ascii_lowercase().as_str() {
                "c" | "continue" => {
                    break;
                }
                "a" | "abort" => {
                    return Err("User aborted commit".into());
                }
                "d" | "diff" => {
                    cmd!("git diff").run()?;
                }
                _ => {
                    println!("Unknown command.");
                }
            }
            print!("{}", instructions);
            stdout().flush()?;

            input.clear();
        }

        let message = format!("Release {}", self.title());

        println!("Creating commit…");
        cmd!("git commit -a -m {message}").read()?;

        println!("Pushing commit…");
        cmd!("git push").read()?;

        Ok(())
    }

    /// Check if the tag for the current version of the crate has been pushed on GitHub.
    fn is_released(&self) -> Result<bool> {
        let response = self.http_client.get(format!(
            "{}/releases/tags/{}",
            GITHUB_API_RUMA,
            self.tag_name()
        ))?;

        Ok(response.status() == StatusCode::OK)
    }

    /// Create the release on GitHub with the given `config` and `credentials`.
    fn release(&self, body: &str) -> Result<()> {
        let request = Request::post(format!("{}/releases", GITHUB_API_RUMA))
            .authentication(Authentication::basic())
            .credentials(Credentials::new(&self.config.user, &self.config.token))
            .header("Accept", "application/vnd.github.v3+json")
            .body(body)?;

        let mut response = self.http_client.send(request)?;

        if response.status() == StatusCode::CREATED {
            Ok(())
        } else {
            Err(format!("{}: {}", response.status(), response.text()?).into())
        }
    }
}

/// A tag from the `GET /repos/{owner}/{repo}/tags` endpoint of GitHub REST API.
#[derive(Debug, Deserialize)]
struct GithubTag {
    /// The name of the tag.
    name: String,
}

/// String manipulations for crate release.
trait StrExt {
    /// Remove soft line breaks as defined in CommonMark spec.
    fn trim_softbreaks(&self) -> String;
}

impl StrExt for str {
    fn trim_softbreaks(&self) -> String {
        let mut string = String::new();
        let mut s = self;

        while let Some(pos) = s.find('\n') {
            string.push_str(&s[..pos]);
            let pos_s = &s[pos..];

            if pos_s.starts_with("\n\n") {
                // Keep new paragraphs (multiple `\n`s).
                let next = pos_s.find(|c: char| c != '\n').unwrap_or(0);
                let (push, next_s) = pos_s.split_at(next);

                string.push_str(push);
                s = next_s;
            } else if s[..pos].ends_with("  ") || s[..pos].ends_with('\\') {
                // Keep hard line breaks (two spaces or a backslash before the line break).
                string.push('\n');
                s = &pos_s[1..];
            } else if let Some(p) = pos_s.find(|c: char| !c.is_ascii_whitespace()) {
                // Keep line break before list items (`\n` + whitespaces + `*` + whitespaces).
                // Remove line break and keep one space otherwise.
                let mut chars = pos_s.char_indices();
                let (_, char) = chars.find(|(i, _)| *i == p).unwrap();

                if char == '*' || char == '-' {
                    match chars.next() {
                        Some((_, next_char)) if next_char.is_ascii_whitespace() => {
                            string.push('\n');
                            s = &pos_s[1..];
                            continue;
                        }
                        _ => {}
                    }
                }

                string.push(' ');
                s = &pos_s[p..];
            }
        }

        string + s
    }
}

/// Extra Version increment methods for crate release.
trait VersionExt {
    /// Adds a pre-release label and number if there is none.
    fn add_pre_release(&mut self);

    /// Increments the pre-release number, if this is a pre-release.
    fn increment_pre_number(&mut self);

    /// Increments the pre-release label from `alpha` to `beta` if this is a pre-release and it is
    /// possible, otherwise does nothing.
    fn increment_pre_label(&mut self);

    /// If the given version can be the next after this one.
    ///
    /// This checks all the version bumps of the format MAJOR.MINOR.PATCH-PRE_LABEL.PRE_NUMBER, with
    /// PRE_LABEL = alpha or beta.
    fn is_next(&self, version: &Version) -> bool;
}

impl VersionExt for Version {
    fn add_pre_release(&mut self) {
        if !self.is_prerelease() {
            self.pre = vec![Identifier::AlphaNumeric("alpha".into()), Identifier::Numeric(1)];
        }
    }

    fn increment_pre_number(&mut self) {
        if self.is_prerelease() {
            if let Identifier::Numeric(n) = self.pre[1] {
                self.pre[1] = Identifier::Numeric(n + 1);
            }
        }
    }

    fn increment_pre_label(&mut self) {
        if self.is_prerelease() {
            match &self.pre[0] {
                Identifier::AlphaNumeric(n) if n == "alpha" => {
                    self.pre =
                        vec![Identifier::AlphaNumeric("beta".into()), Identifier::Numeric(1)];
                }
                _ => {}
            }
        }
    }

    fn is_next(&self, version: &Version) -> bool {
        let mut next = self.clone();

        if self.is_prerelease() {
            next.increment_pre_number();
            if next == *version {
                return true;
            }

            next.increment_pre_label();
            if next == *version {
                return true;
            }

            next.pre = vec![];
            if next == *version {
                return true;
            }
        } else {
            next.increment_patch();
            if next == *version {
                return true;
            }

            next.add_pre_release();
            if next == *version {
                return true;
            }

            next.increment_minor();
            if next == *version {
                return true;
            }

            next.add_pre_release();
            if next == *version {
                return true;
            }

            next.increment_major();
            if next == *version {
                return true;
            }

            next.add_pre_release();
            if next == *version {
                return true;
            }
        }

        false
    }
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::mem;
use std::ptr;

use super::value;
use value::{Value, SIZEOF_PAIR, HEADER_TAG, PAIR_HEADER};

/// An allocator for RustyScheme objects
pub trait Allocator {
    /// Allocates a vector
    fn alloc_vector(& mut self, &[Value]) -> value::Vector;

    /// Allocates a pair
    fn alloc_pair(& mut self, car: Value, cdr: Value);

    /// Allocates a closure
    fn alloc_closure(&mut self,
                     bytecode: &value::BCO,
                     upvalues: &[Value])
                     -> value::Closure;

    /// Allocates a record
    fn alloc_record(&mut self,
                    descriptor: &value::RecordDescriptor,
                    fields: &[Value])
                    -> value::Record;

    /// Allocates a hash table
    fn alloc_hash_table(&mut self, size: usize) -> value::HashTable;

    /// Allocates a port
    fn alloc_port(&mut self, File) -> value::IOPort;

    /// Allocates a rustdata, which contains an arbitrary Rust object
    fn alloc_rustdata<T>(&mut self, object: &T) -> value::RustData;

    // /// Allocates a boxed float on the top of the stack.
    //fn alloc_float(&mut self, float: f64) -> value::Float;
}
#[derive(Debug)]
pub struct Heap {
    tospace: Vec<Value>,
    fromspace: Vec<Value>,
    pub stack: self::Stack,
}

/// Relocates a `Value` in the heap.
///
/// This function relocates a `Value` in the Scheme heap.  It takes two
/// arguments: `current`, the `Value` being relocated, and `end`, the current
/// end of tospace.
///
/// This function takes raw pointers because of aliasing concerns.
unsafe fn relocate(current: *mut Value, end: *mut*mut Value) {
    (*current).size().map(|size| {
        // pointer to head of object being copied
        let pointer = Ptr_Val!(*current);

        // Assert that the object header is nonzero.
        debug_assert!((*pointer).contents != 0,
                      "internal error: copy_value: invalid object header size");
        if (*pointer).contents == HEADER_TAG {
            // Forwarding pointer detected (this header tag is otherwise absurd,
            // since no object can have a size of zero).
            *current = *pointer.offset(1)
        } else {
            // The amount to copy
            let amount_to_copy = (size*size_of!(usize) + 0b111) & !0b111;

            ptr::copy_nonoverlapping(pointer, *end, amount_to_copy);
            *current = Value{ contents: (*end) as usize | (*current).raw_tag() };
            *end = (*end).offset(amount_to_copy as isize >> 3)
        }
    });
}

fn collect(heap: &mut Heap) {
    mem::swap(&mut heap.tospace, &mut heap.fromspace);
    assert!(heap.tospace.capacity() >= heap.fromspace.len());
    //let fromspace_length = heap.fromspace.len(); // Fromspace length
    let tospace_length = heap.tospace.len();
    //assert!(fromspace_length <= tospace_length);
    let ref mut tospace = heap.tospace;
    let mut end_ptr = tospace.as_mut_ptr();
    let start_ptr = end_ptr;

    // We iterate over the stack differently than we iterate over the heap.

    // Stack are all GC roots
    for i in heap.stack.iter_mut() {
        unsafe {
            relocate(i, &mut end_ptr);
            tospace.set_len((end_ptr as usize - start_ptr as usize)/size_of!(usize))
        }
    }

    // Raw pointer to tospace
    let mut current = tospace.as_mut_ptr();
    debug_assert!(end_ptr as usize - current as usize >= heap.stack.len());

    let end_of_tospace = tospace.as_mut_ptr() as usize + tospace_length;

    // Heap consists of contiguous values, so loop over it.
    while (current as usize) < end_of_tospace {
        unsafe {
            let size = mem::transmute::<_, usize>(*current) & !HEADER_TAG;
            current = current.offset(1);
            if !(*current).leafp() {
                for _ in 1..size {
                    relocate(current, &mut end_ptr);
                    current = current.offset(1)
                }
            }
        }
    }

    // Set the size of the new fromspace
    heap.fromspace.resize(((end_of_tospace as u64 - tospace.as_ptr() as u64)/8 *
                          3/2) as usize, unsafe { mem::transmute(1 as usize) });
    let ptr = tospace.as_ptr();
    unsafe {
        tospace.set_len((end_of_tospace - ptr as usize)/size_of!(usize))
    }
}

#[derive(Debug)]
pub struct Stack {
    pub innards: Vec<value::Value>,
}
#[cfg(none)]
impl ::std::ops::Index<usize> for Stack {
    type Output = value::Value;
    fn index(&self, other: u8) -> &value::Value {
        let val: usize = other.into();
        &self.innards[val]
    }
}

use ::std::ops::{Deref, DerefMut};
impl Deref for Stack {
    type Target = Vec<value::Value>;
    fn deref(&self) -> &Vec<value::Value> {
        &self.innards
    }
}

impl DerefMut for Stack {
    fn deref_mut<'a>(&'a mut self) -> &'a mut Vec<value::Value> {
        &mut self.innards
    }
}

impl Heap {
    /// Allocates a Scheme pair, which must be rooted by the caller.
    pub fn alloc_pair(&mut self, car: Value, cdr: Value) {
        let tospace_space = self.tospace.capacity() - self.tospace.len();
        if tospace_space < SIZEOF_PAIR {
            collect(self);
        }
        self.tospace.push(Value{ contents: PAIR_HEADER });
        self.tospace.push(car);
        self.tospace.push(cdr);
        let len = self.tospace.len() - 3;
        let new_value = Value {
            contents: unsafe {
                self.tospace.as_ptr().offset(len as isize) as usize |
                value::PAIR_TAG
            },
        };
        self.stack.push(new_value)
    }
    pub fn new(size: usize) -> Self {
        Heap {
            fromspace: Vec::with_capacity(size),
            tospace: Vec::with_capacity(size),
            stack: Stack{ innards: Vec::with_capacity(1 << 16) },
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use value::*;
    const ZERO: Value = Value { contents: 0, };
    #[test]
    fn can_allocate_objects() {
        let mut heap = Heap::new(1 << 16);
        super::collect(&mut heap);
        println!("HEADER_TAG = {:x}, PAIR_TAG = {:x}, SIZEOF_PAIR = {:x}", HEADER_TAG, PAIR_HEADER, SIZEOF_PAIR);
        heap.alloc_pair(ZERO, ZERO);
        println!("{:?}", heap);
        for i in 1..((1 << 20) - 1000) {
            let old_pair = heap.stack[0];
            heap.alloc_pair(old_pair, old_pair);
            assert_eq!(heap.stack.len(), 2);
            assert_eq!(heap.stack[1].tag(), Tags::Pair);
            heap.stack[0] = heap.stack.pop().unwrap();
            let assert_valid = |heap: &Heap| {
                let new_pair = heap.stack[0];
                assert_eq!(heap.stack[0].tag(), Tags::Pair);
                assert_eq!(new_pair.size(), Some(3));
                if let EnumValue::Pair(ptr) = new_pair.enum_type() {
                    assert_eq!((unsafe { (*ptr).car.get().tag() }), Tags::Pair);
                    assert_eq!((unsafe { (*ptr).cdr.get().tag() }), Tags::Pair);
                } else {
                    unreachable!()
                }
            };
            assert_valid(&heap);
            super::collect(&mut heap);
            assert_valid(&heap);
            assert!(heap.tospace.len() == 3*i)
        }
        assert!(heap.fromspace.capacity() > 3* (1 << 20));
        println!("{:?}", heap);
        {
            super::collect(&mut heap);
        }
    }
}
<commit_msg>No assertion failures, but runs out of mem<commit_after>use std::fs::File;
use std::io::prelude::*;
use std::io::stdout;
use std::mem;
use std::ptr;
#[cfg(not(use_memcpy_in_gc))]
use std::slice;

use super::value;
use value::{Value, SIZEOF_PAIR, HEADER_TAG, PAIR_HEADER};
#[cfg(debug_assertions)]
use value::Tags;
/// An allocator for RustyScheme objects
pub trait Allocator {
    /// Allocates a vector
    fn alloc_vector(&mut self, &[Value]) -> value::Vector;

    /// Allocates a pair
    fn alloc_pair(&mut self, car: Value, cdr: Value);

    /// Allocates a closure
    fn alloc_closure(&mut self, bytecode: &value::BCO, upvalues: &[Value]) -> value::Closure;

    /// Allocates a record
    fn alloc_record(&mut self,
                    descriptor: &value::RecordDescriptor,
                    fields: &[Value])
                    -> value::Record;

    /// Allocates a hash table
    fn alloc_hash_table(&mut self, size: usize) -> value::HashTable;

    /// Allocates a port
    fn alloc_port(&mut self, File) -> value::IOPort;

    /// Allocates a rustdata, which contains an arbitrary Rust object
    fn alloc_rustdata<T>(&mut self, object: &T) -> value::RustData;

// /// Allocates a boxed float on the top of the stack.
// fn alloc_float(&mut self, float: f64) -> value::Float;
}
#[derive(Debug)]
pub struct Heap {
    tospace: Vec<Value>,
    fromspace: Vec<Value>,
    pub stack: self::Stack,
}

/// Consistency checks on the whole heap (in debug mode only) – sloooow.
#[cfg(debug_assertions)]
unsafe fn consistency_check(heap: &Vec<Value>) {
    let assert_in_heap = |heap: &Vec<_>, ptr: usize| {
        assert!(ptr >= heap.as_ptr() as usize);
        let upper_limit = heap.as_ptr() as usize + heap.capacity()*size_of!(Value);
        assert!(ptr < upper_limit,
                "Heap pointer out of range: {} >= {}", ptr, upper_limit)
    };
    let mut index = 0;
    while index < heap.len() {
        let mut current = heap[index];
        let len = current.contents as usize & !HEADER_TAG;
        assert!(len > 0);
        index += 1;
        for _ in 1..len {
            current = heap[index];
            match current.tag() {
                Tags::Num | Tags::Num2 => {
                    assert!(current.contents & 0b11 == 0);
                }
                Tags::Pair => {
                    assert!(current.contents & 0b111 == 0b111);
                    assert!((*Ptr_Val!(current)).contents == PAIR_HEADER);
                    for i in 1..3 {
                        assert_in_heap(heap, Ptr_Val!(current).offset(i)
                                       as usize)
                    }
                }
                Tags::Vector => {
                    assert!(len > 0);
                    for i in 1..len {
                        assert_in_heap(heap, Ptr_Val!(current) as usize + i)
                    }
                }
                _ => unimplemented!(),
            }
            index += 1;
        }
    }
}
#[cfg(not(debug_assertions))]
unsafe fn consistency_check(_heap: &Vec<Value>) {}

/// Relocates a `Value` in the heap.
///
/// This function relocates a `Value` in the Scheme heap.  It takes two
/// arguments: `current`, the `Value` being relocated, and `end`, the current
/// end of tospace.
///
/// This function takes raw pointers because of aliasing concerns.
unsafe fn relocate(current: *mut Value, tospace: &mut Vec<Value>) {
    let size_of_value: usize = size_of!(Value);
    (*current).size().map(|size| {
        // pointer to head of object being copied
        let pointer = Ptr_Val!(*current);

        // Assert that the object header is nonzero.
        debug_assert!((*pointer).contents != 0,
                      "internal error: copy_value: invalid object header size");
        if (*pointer).contents == HEADER_TAG {
            // Forwarding pointer detected (this header tag is otherwise absurd,
            // since no object can have a size of zero).
            *current = *pointer.offset(1)
        } else {
            // End pointer
            let end = tospace.as_mut_ptr().offset(tospace.len() as isize);

            if cfg!(use_memcpy_in_gc) {
                debug_assert!(end as usize & 0b111 == 0);
                // The amount to copy
                let amount_to_copy = (size * size_of_value + 0b111) & !0b111;
                debug_assert!(end as usize + amount_to_copy <=
                              tospace.as_ptr() as usize + tospace.capacity());
                debug_assert!(pointer as usize >= end as usize + amount_to_copy ||
                              pointer as usize + amount_to_copy <= end as usize);
                ptr::copy_nonoverlapping(pointer, end, amount_to_copy);
                *current = Value { contents: end as usize | (*current).raw_tag() };
                let len = tospace.len();
                tospace.set_len(len + amount_to_copy / size_of_value);
            } else {
                let amount_to_copy = ((size * size_of_value + 0b111) & !0b111) / size_of_value;
                debug_assert!(amount_to_copy > 0);
                debug_assert!(end as usize & 0b111 == 0);
                *current = Value { contents: end as usize | ((*current).contents & 0b111) };
                tospace.extend(slice::from_raw_parts(pointer, amount_to_copy));
            }
        }
    });
}

/// Process the heap.
unsafe fn scavange_heap(tospace: &mut Vec<Value>) {
    let mut offset: isize = 0;
    let current = tospace.as_mut_ptr();
    while offset < tospace.len() as isize {
        let size = (*current.offset(offset)).contents & !HEADER_TAG;
        assert!(size > 0);
        offset += 1;
        if !(*current).leafp() {
            for _ in 1..size {
                relocate(current.offset(offset), tospace);
                offset += 1
            }
        }
    }
}

unsafe fn scavange_stack(stack: &mut Vec<Value>, tospace: &mut Vec<Value>) {
    for i in stack.iter_mut() {
        relocate(i, tospace);
    }
}

fn collect(heap: &mut Heap) {
    println!("Initiated garbage collection");
    unsafe {
        consistency_check(&heap.tospace);
        println!("Completed first consistency check");
        mem::swap(&mut heap.tospace, &mut heap.fromspace);
        heap.tospace.reserve(heap.fromspace.len() + heap.fromspace.len()/2);
        println!("Fromspace size is {}", heap.fromspace.len() + heap.fromspace.len() / 2);
        heap.tospace.resize(0, Value{contents:0});
        println!("Tospace resized to {}", heap.tospace.capacity());
        let _ = stdout().flush();
        scavange_stack(&mut heap.stack, &mut heap.tospace);
        println!("Stack scavanged");
        scavange_heap(&mut heap.tospace);
        println!("Heap scavanged");
        consistency_check(&heap.tospace);
        println!("Completed second consistency check");
        //heap.fromspace.resize(0, Value{contents: 0});
    }
}

#[derive(Debug)]
pub struct Stack {
    pub innards: Vec<value::Value>,
}
#[cfg(none)]
impl ::std::ops::Index<usize> for Stack {
    type Output = value::Value;
    fn index(&self, other: u8) -> &value::Value {
        let val: usize = other.into();
        &self.innards[val]
    }
}

use std::ops::{Deref, DerefMut};
impl Deref for Stack {
    type Target = Vec<value::Value>;
    fn deref(&self) -> &Vec<value::Value> {
        &self.innards
    }
}

impl DerefMut for Stack {
    fn deref_mut<'a>(&'a mut self) -> &'a mut Vec<value::Value> {
        &mut self.innards
    }
}

impl Heap {
    /// Allocates a Scheme pair, which must be rooted by the caller.
    pub fn alloc_pair(&mut self, car: Value, cdr: Value) {
        let tospace_space = self.tospace.capacity() - self.tospace.len();
        if tospace_space < SIZEOF_PAIR {
            collect(self);
        }
        self.tospace.push(Value { contents: PAIR_HEADER });
        self.tospace.push(car);
        self.tospace.push(cdr);
        let len = self.tospace.len() - 3;
        let new_value = Value {
            contents: unsafe {
                self.tospace.as_ptr().offset(len as isize) as usize | value::PAIR_TAG
            },
        };
        self.stack.push(new_value);
        println!("Allocated a pair");
    }
    pub fn new(size: usize) -> Self {
        Heap {
            fromspace: Vec::with_capacity(size),
            tospace: Vec::with_capacity(size),
            stack: Stack { innards: Vec::with_capacity(1 << 16) },
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use value::*;
    const ZERO: Value = Value { contents: 0 };
    #[test]
    fn can_allocate_objects() {
        let mut heap = Heap::new(1 << 4);
        super::collect(&mut heap);
        println!("HEADER_TAG = {:x}, PAIR_TAG = {:x}, SIZEOF_PAIR = {:x}",
                 HEADER_TAG,
                 PAIR_HEADER,
                 SIZEOF_PAIR);
        heap.alloc_pair(ZERO, ZERO);
        //println!("{:?}", heap);
        for _ in 1..((1 << 8)) {
            let old_pair = heap.stack[0];
            heap.alloc_pair(old_pair, old_pair);
            assert_eq!(heap.stack.len(), 2);
            assert_eq!(heap.stack[1].tag(), Tags::Pair);
            heap.stack[0] = heap.stack.pop().unwrap();
            let assert_valid = |heap: &Heap| {
                let new_pair = heap.stack[0];
                assert_eq!(heap.stack[0].tag(), Tags::Pair);
                assert_eq!(new_pair.size(), Some(3));
                if let EnumValue::Pair(ptr) = new_pair.enum_type() {
                    assert_eq!((unsafe { (*ptr).car.get().tag() }), Tags::Pair);
                    assert_eq!((unsafe { (*ptr).cdr.get().tag() }), Tags::Pair);
                } else {
                    unreachable!()
                }
            };
            assert_valid(&heap);
            super::collect(&mut heap);
            assert_valid(&heap);
            // assert!(heap.tospace.len() >= 3*i)
        }
        // assert!(heap.fromspace.capacity() > 3* (1 << 20));
        //println!("{:?}", heap);
        {
            super::collect(&mut heap);
        }
    }
}
<|endoftext|>


<commit_before>use core::cmp::{Eq, Ord};
use core::f64::consts::pi;
use funs::triganomic::{cos, sin};
use mat::{Mat3, Mat4};
use num::cast::{NumCast, cast};
use quat::Quat;
use vec::Vec3;

/**
 * The base trait for anglular units
 */
pub trait Angle<T>: Add<self,self>
                  , Sub<self,self>
                  , Mul<T,self>
                  , Div<T,self>
                  , Modulo<T,self>
                  , Neg<self>
                  , Eq, Ord {
    static pure fn full_rotation() -> self;
    static pure fn half_rotation() -> self;
    static pure fn quarter_rotation() -> self;
    static pure fn eighth_rotation() -> self;

    pure fn to_radians() -> Radians<T>;
    pure fn to_degrees() -> Degrees<T>;
    pure fn wrap() -> self;
}

pub enum Radians<T> = T;

pub impl<T:Copy Num NumCast> Radians<T>: Angle<T> {
    #[inline(always)] static pure fn full_rotation()    -> Radians<T> { Radians(move cast(2.0 * pi)) }
    #[inline(always)] static pure fn half_rotation()    -> Radians<T> { Radians(move cast(pi))       }
    #[inline(always)] static pure fn quarter_rotation() -> Radians<T> { Radians(move cast(pi / 2.0)) }
    #[inline(always)] static pure fn eighth_rotation()  -> Radians<T> { Radians(move cast(pi / 4.0)) }

    #[inline(always)] pure fn to_radians() -> Radians<T> { self }
    #[inline(always)] pure fn to_degrees() -> Degrees<T> { Degrees(*self * cast(180.0 / pi)) }

    #[inline(always)] pure fn wrap() -> Radians<T> {
        self % cast(2.0 * pi)   // TODO: keep in the domain of 0 to two_pi
    }
}

pub impl<T:Copy Num> Radians<T>: Add<Radians<T>, Radians<T>> {
    #[inline(always)] pure fn add(rhs: &Radians<T>) -> Radians<T> { Radians(*self + **rhs) }
}

pub impl<T:Copy Num> Radians<T>: Sub<Radians<T>, Radians<T>> {
    #[inline(always)] pure fn sub(rhs: &Radians<T>) -> Radians<T> { Radians(*self - **rhs) }
}

pub impl<T:Copy Num> Radians<T>: Mul<T, Radians<T>> {
    #[inline(always)] pure fn mul(rhs: &T) -> Radians<T> { Radians(*self * *rhs) }
}

pub impl<T:Copy Num> Radians<T>: Div<T, Radians<T>> {
    #[inline(always)] pure fn div(rhs: &T) -> Radians<T> { Radians(*self / *rhs) }
}

pub impl<T:Copy Num> Radians<T>: Modulo<T, Radians<T>> {
    #[inline(always)] pure fn modulo(rhs: &T) -> Radians<T> { Radians(*self % *rhs) }
}

pub impl<T:Copy Num> Radians<T>: Neg<Radians<T>> {
    #[inline(always)] pure fn neg() -> Radians<T> { Radians(-*self) }
}

pub impl<T:Copy Eq> Radians<T>: Eq {
    #[inline(always)] pure fn eq(other: &Radians<T>) -> bool { *self == **other }
    #[inline(always)] pure fn ne(other: &Radians<T>) -> bool { *self != **other }
}

pub impl<T:Copy Ord> Radians<T>: Ord {
    #[inline(always)] pure fn lt(other: &Radians<T>) -> bool { *self <  **other }
    #[inline(always)] pure fn le(other: &Radians<T>) -> bool { *self <= **other }
    #[inline(always)] pure fn ge(other: &Radians<T>) -> bool { *self >= **other }
    #[inline(always)] pure fn gt(other: &Radians<T>) -> bool { *self >  **other }
}

pub enum Degrees<T> = T;

pub impl<T:Copy Num NumCast> Degrees<T>: Angle<T> {
    #[inline(always)] static pure fn full_rotation()    -> Degrees<T> { Degrees(move cast(360.0)) }
    #[inline(always)] static pure fn half_rotation()    -> Degrees<T> { Degrees(move cast(180.0)) }
    #[inline(always)] static pure fn quarter_rotation() -> Degrees<T> { Degrees(move cast(90.0))  }
    #[inline(always)] static pure fn eighth_rotation()  -> Degrees<T> { Degrees(move cast(45.0))  }

    #[inline(always)] pure fn to_radians() -> Radians<T> { Radians(*self * cast(pi / 180.0)) }
    #[inline(always)] pure fn to_degrees() -> Degrees<T> { self }

    #[inline(always)] pure fn wrap() -> Degrees<T> {
        self % cast(360)   // TODO: keep in the domain of 0 to 360
    }
}

pub impl<T:Copy Num> Degrees<T>: Add<Degrees<T>, Degrees<T>> {
    #[inline(always)] pure fn add(rhs: &Degrees<T>) -> Degrees<T> { Degrees(*self + **rhs) }
}

pub impl<T:Copy Num> Degrees<T>: Sub<Degrees<T>, Degrees<T>> {
    #[inline(always)] pure fn sub(rhs: &Degrees<T>) -> Degrees<T> { Degrees(*self - **rhs) }
}

pub impl<T:Copy Num> Degrees<T>: Mul<T, Degrees<T>> {
    #[inline(always)] pure fn mul(rhs: &T) -> Degrees<T> { Degrees(*self * *rhs) }
}

pub impl<T:Copy Num> Degrees<T>: Div<T, Degrees<T>> {
    #[inline(always)] pure fn div(rhs: &T) -> Degrees<T> { Degrees(*self / *rhs) }
}

pub impl<T:Copy Num> Degrees<T>: Modulo<T, Degrees<T>> {
    #[inline(always)] pure fn modulo(rhs: &T) -> Degrees<T> { Degrees(*self % *rhs) }
}

pub impl<T:Copy Num> Degrees<T>: Neg<Degrees<T>> {
    #[inline(always)] pure fn neg() -> Degrees<T> { Degrees(-*self) }
}

pub impl<T:Copy Eq> Degrees<T>: Eq {
    #[inline(always)] pure fn eq(other: &Degrees<T>) -> bool { *self == **other }
    #[inline(always)] pure fn ne(other: &Degrees<T>) -> bool { *self != **other }
}

pub impl<T:Copy Ord> Degrees<T>: Ord {
    #[inline(always)] pure fn lt(other: &Degrees<T>) -> bool { *self <  **other }
    #[inline(always)] pure fn le(other: &Degrees<T>) -> bool { *self <= **other }
    #[inline(always)] pure fn ge(other: &Degrees<T>) -> bool { *self >= **other }
    #[inline(always)] pure fn gt(other: &Degrees<T>) -> bool { *self >  **other }
}

/**
 * An angular rotation around an arbitary axis
 */
pub struct Rotation<T> {
    theta: Radians<T>,
    axis: Vec3<T>,
}

pub impl<T:Copy Num NumCast> Rotation<T> {
    #[inline(always)]
    static pure fn new(theta: Radians<T>, axis: Vec3<T>) -> Rotation<T> {
        Rotation { theta: move theta, axis: move axis }
    }

    #[inline(always)]
    pure fn to_mat3() -> Mat3<T> {
        let c:  T = cos(&self.theta);
        let s:  T = sin(&self.theta);
        let _0: T = cast(0);
        let _1: T = cast(1);
        let t:  T = _1 - c;

        let x = self.axis.x;
        let y = self.axis.y;
        let z = self.axis.z;

        Mat3::new(t * x * x + c,       t * x * y + s * z,   t * x * z - s * y,
                  t * x * y - s * z,   t * y * y + c,       t * y * z + s * x,
                  t * x * z - s - y,   t * y * z - s * x,   t * z * z + c)
    }

    #[inline(always)]
    pure fn to_mat4() -> Mat4<T> {
        self.to_mat3().to_mat4()
    }

    #[inline(always)]
    pure fn to_quat() -> Quat<T> {
        let half = self.theta / cast(2);
        Quat::from_sv(cos(&half), self.axis.mul_t(sin(&half)))
    }
}

pub struct Euler<T> {
    x: Radians<T>,   // pitch
    y: Radians<T>,   // yaw
    z: Radians<T>,   // roll
}<commit_msg>Formatting<commit_after>use core::cmp::{Eq, Ord};
use core::f64::consts::pi;
use funs::triganomic::{cos, sin};
use mat::{Mat3, Mat4};
use num::cast::{NumCast, cast};
use quat::Quat;
use vec::Vec3;

/**
 * The base trait for anglular units
 */
pub trait Angle<T>: Add<self,self>
                  , Sub<self,self>
                  , Mul<T,self>
                  , Div<T,self>
                  , Modulo<T,self>
                  , Neg<self>
                  , Eq, Ord {
    static pure fn full_rotation() -> self;
    static pure fn half_rotation() -> self;
    static pure fn quarter_rotation() -> self;
    static pure fn eighth_rotation() -> self;

    pure fn to_radians() -> Radians<T>;
    pure fn to_degrees() -> Degrees<T>;
    pure fn wrap() -> self;
}

pub enum Radians<T> = T;

pub impl<T:Copy Num NumCast> Radians<T>: Angle<T> {
    #[inline(always)] static pure fn full_rotation()    -> Radians<T> { Radians(move cast(2.0 * pi)) }
    #[inline(always)] static pure fn half_rotation()    -> Radians<T> { Radians(move cast(pi))       }
    #[inline(always)] static pure fn quarter_rotation() -> Radians<T> { Radians(move cast(pi / 2.0)) }
    #[inline(always)] static pure fn eighth_rotation()  -> Radians<T> { Radians(move cast(pi / 4.0)) }

    #[inline(always)] pure fn to_radians() -> Radians<T> { self }
    #[inline(always)] pure fn to_degrees() -> Degrees<T> { Degrees(*self * cast(180.0 / pi)) }

    #[inline(always)] pure fn wrap() -> Radians<T> {
        self % cast(2.0 * pi)   // TODO: keep in the domain of 0 to two_pi
    }
}

pub impl<T:Copy Num> Radians<T>: Add<Radians<T>, Radians<T>> {
    #[inline(always)]
    pure fn add(rhs: &Radians<T>) -> Radians<T> {
        Radians(*self + **rhs)
    }
}

pub impl<T:Copy Num> Radians<T>: Sub<Radians<T>, Radians<T>> {
    #[inline(always)]
    pure fn sub(rhs: &Radians<T>) -> Radians<T> {
        Radians(*self - **rhs)
    }
}

pub impl<T:Copy Num> Radians<T>: Mul<T, Radians<T>> {
    #[inline(always)]
    pure fn mul(rhs: &T) -> Radians<T> {
        Radians(*self * *rhs)
    }
}

pub impl<T:Copy Num> Radians<T>: Div<T, Radians<T>> {
    #[inline(always)]
    pure fn div(rhs: &T) -> Radians<T> {
        Radians(*self / *rhs)
    }
}

pub impl<T:Copy Num> Radians<T>: Modulo<T, Radians<T>> {
    #[inline(always)]
    pure fn modulo(rhs: &T) -> Radians<T> {
        Radians(*self % *rhs)
    }
}

pub impl<T:Copy Num> Radians<T>: Neg<Radians<T>> {
    #[inline(always)]
    pure fn neg() -> Radians<T> {
        Radians(-*self)
    }
}

pub impl<T:Copy Eq> Radians<T>: Eq {
    #[inline(always)] pure fn eq(other: &Radians<T>) -> bool { *self == **other }
    #[inline(always)] pure fn ne(other: &Radians<T>) -> bool { *self != **other }
}

pub impl<T:Copy Ord> Radians<T>: Ord {
    #[inline(always)] pure fn lt(other: &Radians<T>) -> bool { *self <  **other }
    #[inline(always)] pure fn le(other: &Radians<T>) -> bool { *self <= **other }
    #[inline(always)] pure fn ge(other: &Radians<T>) -> bool { *self >= **other }
    #[inline(always)] pure fn gt(other: &Radians<T>) -> bool { *self >  **other }
}

pub enum Degrees<T> = T;

pub impl<T:Copy Num NumCast> Degrees<T>: Angle<T> {
    #[inline(always)] static pure fn full_rotation()    -> Degrees<T> { Degrees(move cast(360.0)) }
    #[inline(always)] static pure fn half_rotation()    -> Degrees<T> { Degrees(move cast(180.0)) }
    #[inline(always)] static pure fn quarter_rotation() -> Degrees<T> { Degrees(move cast(90.0))  }
    #[inline(always)] static pure fn eighth_rotation()  -> Degrees<T> { Degrees(move cast(45.0))  }

    #[inline(always)] pure fn to_radians() -> Radians<T> { Radians(*self * cast(pi / 180.0)) }
    #[inline(always)] pure fn to_degrees() -> Degrees<T> { self }

    #[inline(always)] pure fn wrap() -> Degrees<T> {
        self % cast(360)   // TODO: keep in the domain of 0 to 360
    }
}

pub impl<T:Copy Num> Degrees<T>: Add<Degrees<T>, Degrees<T>> {
    #[inline(always)]
    pure fn add(rhs: &Degrees<T>) -> Degrees<T> {
        Degrees(*self + **rhs)
    }
}

pub impl<T:Copy Num> Degrees<T>: Sub<Degrees<T>, Degrees<T>> {
    #[inline(always)]
    pure fn sub(rhs: &Degrees<T>) -> Degrees<T> {
        Degrees(*self - **rhs)
    }
}

pub impl<T:Copy Num> Degrees<T>: Mul<T, Degrees<T>> {
    #[inline(always)] pure fn mul(rhs: &T) -> Degrees<T> {
        Degrees(*self * *rhs)
    }
}

pub impl<T:Copy Num> Degrees<T>: Div<T, Degrees<T>> {
    #[inline(always)] pure fn div(rhs: &T) -> Degrees<T> {
        Degrees(*self / *rhs)
    }
}

pub impl<T:Copy Num> Degrees<T>: Modulo<T, Degrees<T>> {
    #[inline(always)] pure fn modulo(rhs: &T) -> Degrees<T> {
        Degrees(*self % *rhs)
    }
}

pub impl<T:Copy Num> Degrees<T>: Neg<Degrees<T>> {
    #[inline(always)] pure fn neg() -> Degrees<T> {
        Degrees(-*self)
    }
}

pub impl<T:Copy Eq> Degrees<T>: Eq {
    #[inline(always)] pure fn eq(other: &Degrees<T>) -> bool { *self == **other }
    #[inline(always)] pure fn ne(other: &Degrees<T>) -> bool { *self != **other }
}

pub impl<T:Copy Ord> Degrees<T>: Ord {
    #[inline(always)] pure fn lt(other: &Degrees<T>) -> bool { *self <  **other }
    #[inline(always)] pure fn le(other: &Degrees<T>) -> bool { *self <= **other }
    #[inline(always)] pure fn ge(other: &Degrees<T>) -> bool { *self >= **other }
    #[inline(always)] pure fn gt(other: &Degrees<T>) -> bool { *self >  **other }
}

/**
 * An angular rotation around an arbitary axis
 */
pub struct Rotation<T> {
    theta: Radians<T>,
    axis: Vec3<T>,
}

pub impl<T:Copy Num NumCast> Rotation<T> {
    #[inline(always)]
    static pure fn new(theta: Radians<T>, axis: Vec3<T>) -> Rotation<T> {
        Rotation { theta: move theta, axis: move axis }
    }

    #[inline(always)]
    pure fn to_mat3() -> Mat3<T> {
        let c:  T = cos(&self.theta);
        let s:  T = sin(&self.theta);
        let _0: T = cast(0);
        let _1: T = cast(1);
        let t:  T = _1 - c;

        let x = self.axis.x;
        let y = self.axis.y;
        let z = self.axis.z;

        Mat3::new(t * x * x + c,       t * x * y + s * z,   t * x * z - s * y,
                  t * x * y - s * z,   t * y * y + c,       t * y * z + s * x,
                  t * x * z - s - y,   t * y * z - s * x,   t * z * z + c)
    }

    #[inline(always)]
    pure fn to_mat4() -> Mat4<T> {
        self.to_mat3().to_mat4()
    }

    #[inline(always)]
    pure fn to_quat() -> Quat<T> {
        let half = self.theta / cast(2);
        Quat::from_sv(cos(&half), self.axis.mul_t(sin(&half)))
    }
}

pub struct Euler<T> {
    x: Radians<T>,   // pitch
    y: Radians<T>,   // yaw
    z: Radians<T>,   // roll
}<|endoftext|>