<commit_before>/* diosix hypervisor physical memory management
 *
 * allocate/free contiguous regions of physical memory.
 * these regions are categorized into two groups,
 * depending on the region size.
 *
 * large: >= PHYS_RAM_LARGE_REGION_MIN_SIZE
 * large regions are sized in multiples of
 * PHYS_RAM_LARGE_REGION_MIN_SIZE and are allocated
 * from the top of free region blocks, descending.
 * these are aimed at large blocks of contiguous
 * memory for guest supervisor OSes.
 *
 * small: < PHYS_RAM_LARGE_REGION_MIN_SIZE
 * small regions are sized in multiples of
 * PHYS_RAM_SMALL_REGION_MIN_SIZE and are allocated
 * from the bottom of free region blocks, ascending.
 * these are aimed at small blocks of memory
 * for the hypervisor's private per-CPU heaps.
 *
 * this arrangement is to avoid large and small
 * allocations fragmenting free region blocks
 *
 * (c) Chris Williams, 2019-2021.
 *
 * See LICENSE for usage and copying.
 */

use platform;
use spin::Mutex;
use alloc::vec::Vec;
use platform::physmem::{PhysMemBase, PhysMemEnd, PhysMemSize, AccessPermissions, validate_ram};
use super::error::Cause;
use super::hardware;

/* needed to convert a region into a slice */
use core::slice;

/* to avoid fragmentation, round up physical memory region allocations into multiples of these totals,
depending on the region type. this only applies when creating regions with alloc_region() */
const PHYS_RAM_LARGE_REGION_MIN_SIZE: PhysMemSize = 64 * 1024 * 1024; /* 64MB ought to be enough for anyone */
const PHYS_RAM_SMALL_REGION_MIN_SIZE: PhysMemSize =  1 * 1024 * 1024; /* smaller blocks are multiples of 1MB in size */

/* ensure large region bases are aligned down to multiples of this value
   note: region minimum size must be a non-zero multiple of region base alignment */
const PHYS_RAM_LARGE_REGION_ALIGNMENT: PhysMemSize = 4 * 1024 * 1024; /* 4MB alignment */

/* define whether to split a region N bytes from the top or from the bottom */
#[derive(Clone, Copy, Debug)]
pub enum RegionSplit
{
    FromBottom,
    FromTop
}

/* define whether a region is dirty or clean */
#[derive(Clone, Copy, Debug)]
pub enum RegionHygiene
{
    DontClean, /* don't zero this region */
    CanClean
}

/* describe a physical memory region */
#[derive(Copy, Clone)]
pub struct Region
{
    base: PhysMemBase,
    size: PhysMemSize,
    hygiene: RegionHygiene
}

impl Region
{
    /* create a new region */
    pub fn new(base: PhysMemBase, size: PhysMemSize, hygiene: RegionHygiene) -> Region
    {
        Region
        {
            base,
            size,
            hygiene
        }
    }

    /* scrub a whole region. FIXME: make this less inefficient! */
    pub fn clean(&mut self)
    {
        hvdebug!("Cleaning physical RAM region 0x{:x}, {} bytes", self.base, self.size);

        match self.hygiene
        {
            RegionHygiene::DontClean =>
            {
                hvalert!("BUG: Tried to scrub don't-clean region 0x{:x}", self.base);
                return;
            },
            RegionHygiene::CanClean => self.as_u8_slice().fill(0x0)
        }
    }

    /* fill the end of a region with an array of bytes. thus if the array is 10 bytes long,
    the final 10 bytes of the region will be filled from that array, ascending
    => bytes = array to write into the region
    <= physical address for start of array in the region, or error code */
    pub fn fill_end(&self, bytes: Vec<u8>) -> Result<PhysMemBase, Cause>
    {
        let array_size = bytes.len();

        /* ensure we've got enough space to accomdate the array */
        if self.size < array_size
        {
            return Err(Cause::PhysRegionTooSmall)
        }

        for index in 0..array_size
        {
            unsafe
            {
                *(((self.base + self.size) - (array_size - index)) as *mut u8) = bytes[index];
            }
        }

        Ok((self.base + self.size) - array_size)
    }

    /* allow the currently running supervisor kernel to access this region of physical memory */
    pub fn grant_access(&self)
    {
        platform::physmem::protect(self.base, self.base + self.size, AccessPermissions::ReadWriteExecute);
    }

    /* return or change attributes */
    pub fn base(&self) -> PhysMemBase { self.base }
    pub fn end(&self) -> PhysMemEnd { self.base + self.size }
    pub fn size(&self) -> PhysMemSize { self.size }

    /* represent the region as a word-size or byte-size slice
    **use carefully** don't hold a slice over an IRQ, for example */
    pub fn as_usize_slice(&self) -> &mut [usize]
    {
        unsafe { slice::from_raw_parts_mut(self.base as *mut usize, self.size) }
    }
    pub fn as_u8_slice(&self) -> &mut [u8]
    {
        unsafe { slice::from_raw_parts_mut(self.base as *mut u8, self.size) }
    }

    /* split the region into two portions, lower and upper, and return the two portions.
    maintain the region's hygiene.
    => count = split the region this number of bytes into the block
       measure_from = FromBottom: count is number of bytes from bottom of the block, ascending
                      FromTop: count is number of bytes from the top of the block, descending
    <= return two portions as regions, lower and upper, or a failure code */
    pub fn split(&self, count: PhysMemSize, measure_from: RegionSplit) -> Result<(Region, Region), Cause>
    {
        /* check the split mark is within bounds */
        if count > self.size
        {
            return Err(Cause::PhysRegionSplitOutOfBounds);
        }

        /* return (lower, upper) */
        Ok(match measure_from
        {
            RegionSplit::FromBottom =>
            (
                Region::new(self.base, count, self.hygiene),
                Region::new(self.base + count, self.size - count, self.hygiene)
            ),

            RegionSplit::FromTop =>
            (
                Region::new(self.base, self.size - count, self.hygiene),
                Region::new(self.base + self.size - count, count, self.hygiene)
            ),
        })
    }
}

/* gather up all physical RAM areas from which future capsule and heap physical
RAM allocations will be drawn into the REGIONS list. this list is built from
available, free physical RAM: it must *not* include any RAM areas already in use by
the hypervisor, boot supervisor image, peripherals, etc. the underlying
platform code needs to exclude those off-limits areas.

this list must also be sorted, by base address, lowest first. this is so that
adjoining regions can be merged into one. this list also contains only free
and available regions. if a region is in use, it must be removed from the list. */
lazy_static!
{
    /* acquire REGIONS lock before accessing any physical RAM regions */
    static ref REGIONS: Mutex<SortedRegions> = Mutex::new(SortedRegions::new());
}

/* implement a sorted list of regions */
struct SortedRegions
{
    regions: Vec<Region>
}

impl SortedRegions
{
    /* create an empty list */
    pub fn new() -> SortedRegions
    {
        SortedRegions
        {
            regions: Vec::new()
        }
    }

    /* find a region that has a size equal to or greater than the required size.
       if one is found, remove the region and return it. if one can't be found,
       return an error code. */
    pub fn find(&mut self, required_size: PhysMemSize) -> Result<Region, Cause>
    {
        for index in 0..self.regions.len()
        {
            if self.regions[index].size() >= required_size
            {
                /* remove from the list and return */
                return Ok(self.regions.remove(index));
            }
        }

        Err(Cause::PhysRegionNoMatch) /* can't find a region large enough */
    }

    /* insert a region into the list, sorted by base addresses, lowest first */
    pub fn insert(&mut self, to_insert: Region) -> Result<(), Cause>
    {
        /* ignore zero-size inserts */
        if to_insert.size() == 0
        {
            return Ok(())
        }

        for index in 0..self.regions.len()
        {
            if to_insert.end() <= self.regions[index].base()
            {
                self.regions.insert(index, to_insert);
                return Ok(())
            }

            /* check to make sure we're not adding a region that will collide with another */
            if to_insert.base() >= self.regions[index].base() && to_insert.base() < self.regions[index].end()
            {
                return Err(Cause::PhysRegionCollision);
            }
        }

        /* insert at the end: region greater than all others */
        self.regions.push(to_insert);
        Ok(())
    }

    /* merge all adjoining free regions. this requires the list to be sorted by base address ascending */
    pub fn merge(&mut self)
    {
        let mut cursor = 0;
        loop
        {
            /* prevent search from going out of bounds */
            if (cursor + 1) >= self.regions.len()
            {
                break;
            }

            if self.regions[cursor].end() == self.regions[cursor + 1].base()
            {
                /* absorb the next region's size into this region */
                self.regions[cursor].size = self.regions[cursor].size() + self.regions.remove(cursor + 1).size();
            }
            else
            {
                /* move onto next region */
                cursor = cursor + 1;
            }
        }
    }
}

/* initialize the physical memory system by registering all physical RAM available for use as allocatable regions */
pub fn init() -> Result<(), Cause>
{
    /* we need to know the CPU count so that any memory preallocated or reserved for the cores can be skipped */
    let nr_cpu_cores = match hardware::get_nr_cpu_cores()
    {
        Some(c) => c,
        None => return Err(Cause::PhysicalCoreCountUnknown)
    };

    /* the device tree defines chunks of memory that may or may not be entirely available for use */
    let chunks = match hardware::get_phys_ram_chunks()
    {
        Some(c) => c,
        None => return Err(Cause::PhysNoRAMFound)
    };

    /* iterate over the physical memory chunks... */
    let mut regions = REGIONS.lock();
    for chunk in chunks
    {
        /* ...and let validate_ram break each chunk in sections we can safely use.
        assume the RAM is clean: the firmware or boot code should have wiped it,
        or it should contain random values */
        for section in validate_ram(nr_cpu_cores, chunk)
        {
            regions.insert(Region::new(section.base, section.size, RegionHygiene::CanClean))?;
        }
    }

    Ok(())
}

/* perform housekeeping duties on idle physical CPU cores */
macro_rules! physmemhousekeeper
{
    () => ($crate::physmem::coalesce_regions());
}

pub fn coalesce_regions()
{
    REGIONS.lock().merge();
}

/* allocate a region of available physical memory for guest capsule or hypervisor heap use.
   capsules should use large regions, and the heap should use small, ideally.
   => size = number of bytes for the region, which will be rounded up to next multiple of:
     PHYS_RAM_LARGE_REGION_MIN_SIZE if the size >= PHYS_RAM_LARGE_REGION_MIN_SIZE (large type)
     PHYS_RAM_SMALL_REGION_MIN_SIZE if the size < PHYS_RAM_LARGE_REGION_MIN_SIZE (small type)

     note, large type regions will have a base address aligned down to PHYS_RAM_LARGE_REGION_ALIGNMENT
     this is so that guests that require 2MB or 4MB kernel alignment (eg RV64GC Linux) work as expected
     see: https://patchwork.kernel.org/patch/10868465/
     this code assumes the top of physically available RAM is aligned to PHYS_RAM_LARGE_REGION_ALIGNMENT

   <= Region structure for the space, or an error code */
pub fn alloc_region(size: PhysMemSize) -> Result<Region, Cause>
{
    /* determine where to split the free region block, and the region type */
    let (split_from, region_multiple) = if size >= PHYS_RAM_LARGE_REGION_MIN_SIZE
    {
        (RegionSplit::FromTop, PHYS_RAM_LARGE_REGION_MIN_SIZE)
    }
    else
    {
        (RegionSplit::FromBottom, PHYS_RAM_SMALL_REGION_MIN_SIZE)
    };

    /* round up to a multiple of the minimum size of a region type to avoid fragmentation */
    let adjusted_size = match size % region_multiple
    {
        0 => size,
        d => (size - d) + region_multiple
    };

    let mut regions = REGIONS.lock();
    match regions.find(adjusted_size) // find will remove found region from free list if successful
    {
        Ok(found) =>
        {
            /* split the found region into two parts: one portion for the newly
            allocated region, and the remaining portion is returned to the free list.
            adjusted_size defines whwre in the region the split point occurs.
            split_from defines whether adjusted_size is measured from the top or
            bottom of the region block */
            match (found.split(adjusted_size, split_from), split_from)
            {
                /* split so that the lower portion is allocated, and the upper portion is returned to the free list */
                (Ok((mut lower, upper)), RegionSplit::FromBottom) =>
                {
                    regions.insert(upper)?;
                    lower.clean();
                    Ok(lower)
                },

                /* split so that the upper portion is allocated, and the lower portion is returned to the free list */
                (Ok((lower, upper)), RegionSplit::FromTop) =>
                {
                    /* bring the base of the upper portion down to alignment mark */
                    let mut aligned_upper = match upper.base % PHYS_RAM_LARGE_REGION_ALIGNMENT
                    {
                        0 => Region::new(upper.base, upper.size, found.hygiene),
                        d => Region::new(upper.base - d, upper.size + d, found.hygiene)
                    };

                    /* fail out if upper portion crashes through the lower portion base after alignment */
                    if lower.size < aligned_upper.size - upper.size
                    {
                        return Err(Cause::PhysRegionRegionAlignmentFailure)
                    }

                    /* adjust the size of the lower portion if the upper portion was aligned down */
                    let adjusted_lower = match aligned_upper.size - upper.size
                    {
                        0 => lower,
                        d => Region::new(lower.base, lower.size - d, found.hygiene)
                    };

                    regions.insert(adjusted_lower)?;
                    aligned_upper.clean();
                    Ok(aligned_upper)
                },

                (Err(e), _) => Err(e)
            }
        },
        Err(_) => Err(Cause::PhysNotEnoughFreeRAM)
    }
}

/* deallocate a region so that its physical RAM can be reallocated.
   only accept samll regions that are multiples of PHYS_RAM_SMALL_REGION_MIN_SIZE
   and large regions that are multiples of PHYS_RAM_LARGE_REGION_MIN_SIZE
   => to_free = region to deallocate
   <= Ok for success, or an error code for failure */
pub fn dealloc_region(to_free: Region) -> Result<(), Cause>
{
    let size = to_free.size();

    /* police the size of the region */
    if size < PHYS_RAM_LARGE_REGION_MIN_SIZE
    {
        if size % PHYS_RAM_SMALL_REGION_MIN_SIZE != 0
        {
            return Err(Cause::PhysRegionSmallNotMultiple);
        }
    }
    else
    {
        if size % PHYS_RAM_LARGE_REGION_MIN_SIZE != 0
        {
            return Err(Cause::PhysRegionLargeNotMultiple);
        }
    }

    REGIONS.lock().insert(to_free)
}
<commit_msg>* Use built-in mutexes rather than spin-rs * Only zero allocated physical RAM regions in release mode as right now it's quite inefficient and the majority of development work is done in debug mode WIP / TODO: accelerate memory region scrubbing<commit_after>/* diosix hypervisor physical memory management
 *
 * allocate/free contiguous regions of physical memory.
 * these regions are categorized into two groups,
 * depending on the region size.
 *
 * large: >= PHYS_RAM_LARGE_REGION_MIN_SIZE
 * large regions are sized in multiples of
 * PHYS_RAM_LARGE_REGION_MIN_SIZE and are allocated
 * from the top of free region blocks, descending.
 * these are aimed at large blocks of contiguous
 * memory for guest supervisor OSes.
 *
 * small: < PHYS_RAM_LARGE_REGION_MIN_SIZE
 * small regions are sized in multiples of
 * PHYS_RAM_SMALL_REGION_MIN_SIZE and are allocated
 * from the bottom of free region blocks, ascending.
 * these are aimed at small blocks of memory
 * for the hypervisor's private per-CPU heaps.
 *
 * this arrangement is to avoid large and small
 * allocations fragmenting free region blocks
 *
 * (c) Chris Williams, 2019-2021.
 *
 * See LICENSE for usage and copying.
 */

use platform;
use super::lock::Mutex;
use alloc::vec::Vec;
use platform::physmem::{PhysMemBase, PhysMemEnd, PhysMemSize, AccessPermissions, validate_ram};
use super::error::Cause;
use super::hardware;

/* needed to convert a region into a slice */
use core::slice;

/* to avoid fragmentation, round up physical memory region allocations into multiples of these totals,
depending on the region type. this only applies when creating regions with alloc_region() */
const PHYS_RAM_LARGE_REGION_MIN_SIZE: PhysMemSize = 64 * 1024 * 1024; /* 64MB ought to be enough for anyone */
const PHYS_RAM_SMALL_REGION_MIN_SIZE: PhysMemSize =  1 * 1024 * 1024; /* smaller blocks are multiples of 1MB in size */

/* ensure large region bases are aligned down to multiples of this value
   note: region minimum size must be a non-zero multiple of region base alignment */
const PHYS_RAM_LARGE_REGION_ALIGNMENT: PhysMemSize = 4 * 1024 * 1024; /* 4MB alignment */

/* define whether to split a region N bytes from the top or from the bottom */
#[derive(Clone, Copy, Debug)]
pub enum RegionSplit
{
    FromBottom,
    FromTop
}

/* define whether a region is dirty or clean */
#[derive(Clone, Copy, Debug)]
pub enum RegionHygiene
{
    DontClean, /* don't zero this region */
    CanClean
}

/* describe a physical memory region */
#[derive(Copy, Clone)]
pub struct Region
{
    base: PhysMemBase,
    size: PhysMemSize,
    hygiene: RegionHygiene
}

impl Region
{
    /* create a new region */
    pub fn new(base: PhysMemBase, size: PhysMemSize, hygiene: RegionHygiene) -> Region
    {
        Region
        {
            base,
            size,
            hygiene
        }
    }

    /* scrub a whole region. FIXME: make this fast and efficient!
    Note: this only zeroes the region in release mode to avoid delays
    in debugging/development with slow region zeroing */
    pub fn clean(&mut self)
    {
        match self.hygiene
        {
            RegionHygiene::DontClean =>
            {
                hvalert!("BUG: Tried to scrub don't-clean region 0x{:x}", self.base);
                return;
            },
            RegionHygiene::CanClean =>
            {
                #[cfg(not(debug_assertions))]
                self.as_u8_slice().fill(0x0);
            }
        }
    }

    /* fill the end of a region with an array of bytes. thus if the array is 10 bytes long,
    the final 10 bytes of the region will be filled from that array, ascending
    => bytes = array to write into the region
    <= physical address for start of array in the region, or error code */
    pub fn fill_end(&self, bytes: Vec<u8>) -> Result<PhysMemBase, Cause>
    {
        let array_size = bytes.len();

        /* ensure we've got enough space to accomdate the array */
        if self.size < array_size
        {
            return Err(Cause::PhysRegionTooSmall)
        }

        for index in 0..array_size
        {
            unsafe
            {
                *(((self.base + self.size) - (array_size - index)) as *mut u8) = bytes[index];
            }
        }

        Ok((self.base + self.size) - array_size)
    }

    /* allow the currently running supervisor kernel to access this region of physical memory */
    pub fn grant_access(&self)
    {
        platform::physmem::protect(self.base, self.base + self.size, AccessPermissions::ReadWriteExecute);
    }

    /* return or change attributes */
    pub fn base(&self) -> PhysMemBase { self.base }
    pub fn end(&self) -> PhysMemEnd { self.base + self.size }
    pub fn size(&self) -> PhysMemSize { self.size }

    /* represent the region as a word-size or byte-size slice
    **use carefully** don't hold a slice over an IRQ, for example */
    pub fn as_usize_slice(&self) -> &mut [usize]
    {
        unsafe { slice::from_raw_parts_mut(self.base as *mut usize, self.size) }
    }
    pub fn as_u8_slice(&self) -> &mut [u8]
    {
        unsafe { slice::from_raw_parts_mut(self.base as *mut u8, self.size) }
    }

    /* split the region into two portions, lower and upper, and return the two portions.
    maintain the region's hygiene.
    => count = split the region this number of bytes into the block
       measure_from = FromBottom: count is number of bytes from bottom of the block, ascending
                      FromTop: count is number of bytes from the top of the block, descending
    <= return two portions as regions, lower and upper, or a failure code */
    pub fn split(&self, count: PhysMemSize, measure_from: RegionSplit) -> Result<(Region, Region), Cause>
    {
        /* check the split mark is within bounds */
        if count > self.size
        {
            return Err(Cause::PhysRegionSplitOutOfBounds);
        }

        /* return (lower, upper) */
        Ok(match measure_from
        {
            RegionSplit::FromBottom =>
            (
                Region::new(self.base, count, self.hygiene),
                Region::new(self.base + count, self.size - count, self.hygiene)
            ),

            RegionSplit::FromTop =>
            (
                Region::new(self.base, self.size - count, self.hygiene),
                Region::new(self.base + self.size - count, count, self.hygiene)
            ),
        })
    }
}

/* gather up all physical RAM areas from which future capsule and heap physical
RAM allocations will be drawn into the REGIONS list. this list is built from
available, free physical RAM: it must *not* include any RAM areas already in use by
the hypervisor, boot supervisor image, peripherals, etc. the underlying
platform code needs to exclude those off-limits areas.

this list must also be sorted, by base address, lowest first. this is so that
adjoining regions can be merged into one. this list also contains only free
and available regions. if a region is in use, it must be removed from the list. */
lazy_static!
{
    /* acquire REGIONS lock before accessing any physical RAM regions */
    static ref REGIONS: Mutex<SortedRegions> = Mutex::new(SortedRegions::new());
}

/* implement a sorted list of regions */
struct SortedRegions
{
    regions: Vec<Region>
}

impl SortedRegions
{
    /* create an empty list */
    pub fn new() -> SortedRegions
    {
        SortedRegions
        {
            regions: Vec::new()
        }
    }

    /* find a region that has a size equal to or greater than the required size.
       if one is found, remove the region and return it. if one can't be found,
       return an error code. */
    pub fn find(&mut self, required_size: PhysMemSize) -> Result<Region, Cause>
    {
        for index in 0..self.regions.len()
        {
            if self.regions[index].size() >= required_size
            {
                /* remove from the list and return */
                return Ok(self.regions.remove(index));
            }
        }

        Err(Cause::PhysRegionNoMatch) /* can't find a region large enough */
    }

    /* insert a region into the list, sorted by base addresses, lowest first */
    pub fn insert(&mut self, to_insert: Region) -> Result<(), Cause>
    {
        /* ignore zero-size inserts */
        if to_insert.size() == 0
        {
            return Ok(())
        }

        for index in 0..self.regions.len()
        {
            if to_insert.end() <= self.regions[index].base()
            {
                self.regions.insert(index, to_insert);
                return Ok(())
            }

            /* check to make sure we're not adding a region that will collide with another */
            if to_insert.base() >= self.regions[index].base() && to_insert.base() < self.regions[index].end()
            {
                return Err(Cause::PhysRegionCollision);
            }
        }

        /* insert at the end: region greater than all others */
        self.regions.push(to_insert);
        Ok(())
    }

    /* merge all adjoining free regions. this requires the list to be sorted by base address ascending */
    pub fn merge(&mut self)
    {
        let mut cursor = 0;
        loop
        {
            /* prevent search from going out of bounds */
            if (cursor + 1) >= self.regions.len()
            {
                break;
            }

            if self.regions[cursor].end() == self.regions[cursor + 1].base()
            {
                /* absorb the next region's size into this region */
                self.regions[cursor].size = self.regions[cursor].size() + self.regions.remove(cursor + 1).size();
            }
            else
            {
                /* move onto next region */
                cursor = cursor + 1;
            }
        }
    }
}

/* initialize the physical memory system by registering all physical RAM available for use as allocatable regions */
pub fn init() -> Result<(), Cause>
{
    /* we need to know the CPU count so that any memory preallocated or reserved for the cores can be skipped */
    let nr_cpu_cores = match hardware::get_nr_cpu_cores()
    {
        Some(c) => c,
        None => return Err(Cause::PhysicalCoreCountUnknown)
    };

    /* the device tree defines chunks of memory that may or may not be entirely available for use */
    let chunks = match hardware::get_phys_ram_chunks()
    {
        Some(c) => c,
        None => return Err(Cause::PhysNoRAMFound)
    };

    /* iterate over the physical memory chunks... */
    let mut regions = REGIONS.lock();
    for chunk in chunks
    {
        /* ...and let validate_ram break each chunk in sections we can safely use.
        assume the RAM is clean: the firmware or boot code should have wiped it,
        or it should contain random values */
        for section in validate_ram(nr_cpu_cores, chunk)
        {
            regions.insert(Region::new(section.base, section.size, RegionHygiene::CanClean))?;
        }
    }

    Ok(())
}

/* perform housekeeping duties on idle physical CPU cores */
macro_rules! physmemhousekeeper
{
    () => ($crate::physmem::coalesce_regions());
}

pub fn coalesce_regions()
{
    REGIONS.lock().merge();
}

/* allocate a region of available physical memory for guest capsule or hypervisor heap use.
   capsules should use large regions, and the heap should use small, ideally.
   => size = number of bytes for the region, which will be rounded up to next multiple of:
     PHYS_RAM_LARGE_REGION_MIN_SIZE if the size >= PHYS_RAM_LARGE_REGION_MIN_SIZE (large type)
     PHYS_RAM_SMALL_REGION_MIN_SIZE if the size < PHYS_RAM_LARGE_REGION_MIN_SIZE (small type)

     note, large type regions will have a base address aligned down to PHYS_RAM_LARGE_REGION_ALIGNMENT
     this is so that guests that require 2MB or 4MB kernel alignment (eg RV64GC Linux) work as expected
     see: https://patchwork.kernel.org/patch/10868465/
     this code assumes the top of physically available RAM is aligned to PHYS_RAM_LARGE_REGION_ALIGNMENT

   <= Region structure for the space, or an error code */
pub fn alloc_region(size: PhysMemSize) -> Result<Region, Cause>
{
    /* determine where to split the free region block, and the region type */
    let (split_from, region_multiple) = if size >= PHYS_RAM_LARGE_REGION_MIN_SIZE
    {
        (RegionSplit::FromTop, PHYS_RAM_LARGE_REGION_MIN_SIZE)
    }
    else
    {
        (RegionSplit::FromBottom, PHYS_RAM_SMALL_REGION_MIN_SIZE)
    };

    /* round up to a multiple of the minimum size of a region type to avoid fragmentation */
    let adjusted_size = match size % region_multiple
    {
        0 => size,
        d => (size - d) + region_multiple
    };

    let mut regions = REGIONS.lock();
    match regions.find(adjusted_size) // find will remove found region from free list if successful
    {
        Ok(found) =>
        {
            /* split the found region into two parts: one portion for the newly
            allocated region, and the remaining portion is returned to the free list.
            adjusted_size defines whwre in the region the split point occurs.
            split_from defines whether adjusted_size is measured from the top or
            bottom of the region block */
            match (found.split(adjusted_size, split_from), split_from)
            {
                /* split so that the lower portion is allocated, and the upper portion is returned to the free list */
                (Ok((mut lower, upper)), RegionSplit::FromBottom) =>
                {
                    regions.insert(upper)?;
                    lower.clean();
                    Ok(lower)
                },

                /* split so that the upper portion is allocated, and the lower portion is returned to the free list */
                (Ok((lower, upper)), RegionSplit::FromTop) =>
                {
                    /* bring the base of the upper portion down to alignment mark */
                    let mut aligned_upper = match upper.base % PHYS_RAM_LARGE_REGION_ALIGNMENT
                    {
                        0 => Region::new(upper.base, upper.size, found.hygiene),
                        d => Region::new(upper.base - d, upper.size + d, found.hygiene)
                    };

                    /* fail out if upper portion crashes through the lower portion base after alignment */
                    if lower.size < aligned_upper.size - upper.size
                    {
                        return Err(Cause::PhysRegionRegionAlignmentFailure)
                    }

                    /* adjust the size of the lower portion if the upper portion was aligned down */
                    let adjusted_lower = match aligned_upper.size - upper.size
                    {
                        0 => lower,
                        d => Region::new(lower.base, lower.size - d, found.hygiene)
                    };

                    regions.insert(adjusted_lower)?;
                    aligned_upper.clean();
                    Ok(aligned_upper)
                },

                (Err(e), _) => Err(e)
            }
        },
        Err(_) => Err(Cause::PhysNotEnoughFreeRAM)
    }
}

/* deallocate a region so that its physical RAM can be reallocated.
   only accept samll regions that are multiples of PHYS_RAM_SMALL_REGION_MIN_SIZE
   and large regions that are multiples of PHYS_RAM_LARGE_REGION_MIN_SIZE
   => to_free = region to deallocate
   <= Ok for success, or an error code for failure */
pub fn dealloc_region(to_free: Region) -> Result<(), Cause>
{
    let size = to_free.size();

    /* police the size of the region */
    if size < PHYS_RAM_LARGE_REGION_MIN_SIZE
    {
        if size % PHYS_RAM_SMALL_REGION_MIN_SIZE != 0
        {
            return Err(Cause::PhysRegionSmallNotMultiple);
        }
    }
    else
    {
        if size % PHYS_RAM_LARGE_REGION_MIN_SIZE != 0
        {
            return Err(Cause::PhysRegionLargeNotMultiple);
        }
    }

    REGIONS.lock().insert(to_free)
}
<|endoftext|>


<commit_before>//! # crateinfo
//!
//! Loads crate information.
//!

#[cfg(test)]
#[path = "crateinfo_test.rs"]
mod crateinfo_test;

use crate::command;
use crate::types::{CargoMetadata, CrateDependency, CrateInfo};
use fsio;
use glob::glob;
use serde::{Deserialize, Deserializer};
use serde_json;
use std::env;
use std::path::{Path, PathBuf};
use std::process::Command;

fn expand_glob_members(glob_member: &str) -> Vec<String> {
    match glob(glob_member) {
        Ok(entries) => {
            let mut members = vec![];

            for entry in entries {
                match entry {
                    Ok(path) => {
                        let mut updated_path = path.to_str().unwrap().to_string();
                        updated_path = updated_path.replace("\\", "/");
                        members.push(updated_path);
                    }
                    _ => (),
                };
            }

            members
        }
        _ => vec![],
    }
}

fn normalize_members(crate_info: &mut CrateInfo) {
    match crate_info.workspace {
        Some(ref mut workspace) => {
            match workspace.members {
                Some(ref mut members) => {
                    let existing_members = members.clone();

                    let mut index = 0;
                    for member in existing_members.iter() {
                        // glob
                        if member.contains("*") {
                            let mut expanded_members = expand_glob_members(&member);

                            members.remove(index);
                            members.append(&mut expanded_members);
                        } else {
                            index = index + 1;
                        }
                    }
                }
                None => (),
            };
        }
        None => (), //not a workspace
    }
}

fn get_members_from_dependencies(crate_info: &CrateInfo) -> Vec<String> {
    let mut members = vec![];

    match crate_info.dependencies {
        Some(ref dependencies) => {
            for value in dependencies.values() {
                match *value {
                    CrateDependency::Info(ref info) => match info.path {
                        Some(ref path) => {
                            if path.starts_with("./") {
                                let member_path =
                                    path.chars().skip(2).take(path.len() - 2).collect();
                                members.push(member_path);
                            }
                        }
                        None => (),
                    },
                    _ => (),
                };
            }
        }
        None => (),
    };

    members
}

fn add_members(crate_info: &mut CrateInfo, new_members: Vec<String>) {
    if new_members.len() > 0 {
        match crate_info.workspace {
            Some(ref mut workspace) => match workspace.members {
                Some(ref mut members) => {
                    for new_member in new_members.iter() {
                        let member_string = new_member.to_string();

                        match members.iter().position(|member| *member == member_string) {
                            None => members.push(member_string),
                            _ => (),
                        }
                    }
                }
                None => workspace.members = Some(new_members),
            },
            None => (), //not a workspace
        }
    }
}

fn remove_excludes(crate_info: &mut CrateInfo) -> bool {
    let mut removed = false;

    match crate_info.workspace {
        Some(ref mut workspace) => match workspace.exclude {
            Some(ref excludes) => match workspace.members {
                Some(ref mut members) => {
                    for exclude in excludes.iter() {
                        let exclude_string = exclude.to_string();

                        let result = members.iter().position(|member| *member == exclude_string);
                        match result {
                            Some(index) => {
                                members.remove(index);

                                removed = true;

                                ()
                            }
                            None => (),
                        };
                    }
                }
                None => (),
            },
            None => (),
        },
        None => (), //not a workspace
    };

    removed
}

fn load_workspace_members(crate_info: &mut CrateInfo) {
    if crate_info.workspace.is_some() {
        normalize_members(crate_info);

        let dependencies = get_members_from_dependencies(&crate_info);

        add_members(crate_info, dependencies);

        remove_excludes(crate_info);
    }
}

/// Loads the crate info based on the Cargo.toml found in the current working directory.
pub(crate) fn load() -> CrateInfo {
    load_from(Path::new("Cargo.toml").to_path_buf())
}

pub(crate) fn load_from(file_path: PathBuf) -> CrateInfo {
    if file_path.exists() {
        debug!("Reading file: {:#?}", &file_path);
        let crate_info_string = match fsio::file::read_text_file(&file_path) {
            Ok(content) => content,
            Err(error) => panic!("Unable to open Cargo.toml, error: {}", error),
        };

        let mut crate_info: CrateInfo = match toml::from_str(&crate_info_string) {
            Ok(value) => value,
            Err(error) => panic!("Unable to parse Cargo.toml, {}", error),
        };

        load_workspace_members(&mut crate_info);

        debug!("Loaded Cargo.toml: {:#?}", &crate_info);

        crate_info
    } else {
        CrateInfo::new()
    }
}

#[derive(Debug, Deserialize)]
struct CargoConfig {
    build: Option<CargoConfigBuild>,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
struct CargoConfigBuild {
    #[serde(default, deserialize_with = "deserialize_target")]
    target: Option<String>,
    target_dir: Option<PathBuf>,
}

fn deserialize_target<'de, D: Deserializer<'de>>(
    deserializer: D,
) -> Result<Option<String>, D::Error> {
    Option::<String>::deserialize(deserializer)?
        .map(|target| target.trim_end_matches(".json").to_string())
        .map(Ok)
        .transpose()
}

fn get_cargo_config(home: Option<PathBuf>) -> Option<CargoConfig> {
    let path = env::current_dir().ok()?;

    let config_file = path
        .ancestors()
        .map(|ancestor| ancestor.join(".cargo"))
        .chain(home)
        .map(|config_file| config_file.join("config"))
        .filter_map(|config_file| {
            let config_file_with_extension = config_file.with_extension("toml");

            if config_file.exists() {
                Some(config_file)
            } else if config_file_with_extension.exists() {
                Some(config_file_with_extension)
            } else {
                None
            }
        })
        .next()?;

    let config_file = fsio::file::read_text_file(&config_file).ok()?;
    toml::from_str(&config_file).ok()
}

pub(crate) fn crate_target_triple(
    default_target_triple: Option<String>,
    home: Option<PathBuf>,
) -> Option<String> {
    get_cargo_config(home)
        .and_then(|config| config.build)
        .and_then(|build| build.target)
        .or(default_target_triple)
}

pub(crate) fn crate_target_dir(home: Option<PathBuf>) -> String {
    env::var("CARGO_TARGET_DIR").ok().unwrap_or_else(|| {
        let build = get_cargo_config(home).and_then(|config| config.build);
        let (target_dir, target_triple) = if let Some(build) = build {
            (build.target_dir, build.target)
        } else {
            (None, None)
        };
        let target_dir = target_dir.unwrap_or_else(|| "target".into());
        if let Some(target_triple) = target_triple {
            target_dir.join(target_triple)
        } else {
            target_dir
        }
        .display()
        .to_string()
    })
}

pub(crate) fn search_workspace_root() -> Option<String> {
    if envmnt::is("CARGO_MAKE_WORKSPACE_EMULATION") {
        search_workspace_root_for_emulation()
    } else {
        search_workspace_root_via_metadata()
    }
}

fn search_workspace_root_for_emulation() -> Option<String> {
    let path_value = envmnt::get_any(
        &vec![
            "CARGO_MAKE_WORKSPACE_EMULATION_ROOT_DIRECTORY",
            "CARGO_MAKE_WORKING_DIRECTORY",
        ],
        "",
    );

    if path_value.is_empty() {
        None
    } else {
        Some(path_value)
    }
}

fn search_workspace_root_via_metadata() -> Option<String> {
    debug!("Getting cargo metadata.");

    let mut command_struct = Command::new("cargo");
    let result = command_struct.arg("metadata").output();

    match result {
        Ok(output) => {
            let exit_code = command::get_exit_code(Ok(output.status), false);

            if exit_code == 0 {
                let stdout = String::from_utf8_lossy(&output.stdout);
                let metadata: CargoMetadata = match serde_json::from_str(&stdout) {
                    Ok(metadata) => metadata,
                    Err(error) => {
                        debug!("Unable to extract cargo metadata, error: {:#?}", &error);
                        CargoMetadata::new()
                    }
                };

                metadata.workspace_root
            } else {
                debug!(
                    "Unable to extract cargo metadata, error code: {}",
                    exit_code
                );
                None
            }
        }
        Err(error) => {
            debug!("Unable to extract cargo metadata, error: {:#?}", &error);
            None
        }
    }
}
<commit_msg>Introduce RustTarget to cope with future changes better<commit_after>//! # crateinfo
//!
//! Loads crate information.
//!

#[cfg(test)]
#[path = "crateinfo_test.rs"]
mod crateinfo_test;

use crate::command;
use crate::types::{CargoMetadata, CrateDependency, CrateInfo};
use fsio;
use glob::glob;
use serde_json;
use std::env;
use std::ffi::OsStr;
use std::path::{Path, PathBuf};
use std::process::Command;

fn expand_glob_members(glob_member: &str) -> Vec<String> {
    match glob(glob_member) {
        Ok(entries) => {
            let mut members = vec![];

            for entry in entries {
                match entry {
                    Ok(path) => {
                        let mut updated_path = path.to_str().unwrap().to_string();
                        updated_path = updated_path.replace("\\", "/");
                        members.push(updated_path);
                    }
                    _ => (),
                };
            }

            members
        }
        _ => vec![],
    }
}

fn normalize_members(crate_info: &mut CrateInfo) {
    match crate_info.workspace {
        Some(ref mut workspace) => {
            match workspace.members {
                Some(ref mut members) => {
                    let existing_members = members.clone();

                    let mut index = 0;
                    for member in existing_members.iter() {
                        // glob
                        if member.contains("*") {
                            let mut expanded_members = expand_glob_members(&member);

                            members.remove(index);
                            members.append(&mut expanded_members);
                        } else {
                            index = index + 1;
                        }
                    }
                }
                None => (),
            };
        }
        None => (), //not a workspace
    }
}

fn get_members_from_dependencies(crate_info: &CrateInfo) -> Vec<String> {
    let mut members = vec![];

    match crate_info.dependencies {
        Some(ref dependencies) => {
            for value in dependencies.values() {
                match *value {
                    CrateDependency::Info(ref info) => match info.path {
                        Some(ref path) => {
                            if path.starts_with("./") {
                                let member_path =
                                    path.chars().skip(2).take(path.len() - 2).collect();
                                members.push(member_path);
                            }
                        }
                        None => (),
                    },
                    _ => (),
                };
            }
        }
        None => (),
    };

    members
}

fn add_members(crate_info: &mut CrateInfo, new_members: Vec<String>) {
    if new_members.len() > 0 {
        match crate_info.workspace {
            Some(ref mut workspace) => match workspace.members {
                Some(ref mut members) => {
                    for new_member in new_members.iter() {
                        let member_string = new_member.to_string();

                        match members.iter().position(|member| *member == member_string) {
                            None => members.push(member_string),
                            _ => (),
                        }
                    }
                }
                None => workspace.members = Some(new_members),
            },
            None => (), //not a workspace
        }
    }
}

fn remove_excludes(crate_info: &mut CrateInfo) -> bool {
    let mut removed = false;

    match crate_info.workspace {
        Some(ref mut workspace) => match workspace.exclude {
            Some(ref excludes) => match workspace.members {
                Some(ref mut members) => {
                    for exclude in excludes.iter() {
                        let exclude_string = exclude.to_string();

                        let result = members.iter().position(|member| *member == exclude_string);
                        match result {
                            Some(index) => {
                                members.remove(index);

                                removed = true;

                                ()
                            }
                            None => (),
                        };
                    }
                }
                None => (),
            },
            None => (),
        },
        None => (), //not a workspace
    };

    removed
}

fn load_workspace_members(crate_info: &mut CrateInfo) {
    if crate_info.workspace.is_some() {
        normalize_members(crate_info);

        let dependencies = get_members_from_dependencies(&crate_info);

        add_members(crate_info, dependencies);

        remove_excludes(crate_info);
    }
}

/// Loads the crate info based on the Cargo.toml found in the current working directory.
pub(crate) fn load() -> CrateInfo {
    load_from(Path::new("Cargo.toml").to_path_buf())
}

pub(crate) fn load_from(file_path: PathBuf) -> CrateInfo {
    if file_path.exists() {
        debug!("Reading file: {:#?}", &file_path);
        let crate_info_string = match fsio::file::read_text_file(&file_path) {
            Ok(content) => content,
            Err(error) => panic!("Unable to open Cargo.toml, error: {}", error),
        };

        let mut crate_info: CrateInfo = match toml::from_str(&crate_info_string) {
            Ok(value) => value,
            Err(error) => panic!("Unable to parse Cargo.toml, {}", error),
        };

        load_workspace_members(&mut crate_info);

        debug!("Loaded Cargo.toml: {:#?}", &crate_info);

        crate_info
    } else {
        CrateInfo::new()
    }
}

#[derive(Debug, Deserialize)]
struct CargoConfig {
    build: Option<CargoConfigBuild>,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
struct CargoConfigBuild {
    target: Option<RustTarget>,
    target_dir: Option<PathBuf>,
}

#[derive(Debug, Deserialize)]
#[serde(from = "PathBuf")]
struct RustTarget(PathBuf);

impl RustTarget {
    fn name(&self) -> &str {
        self.0.file_stem().unwrap().to_str().unwrap()
    }
}

impl From<PathBuf> for RustTarget {
    fn from(buf: PathBuf) -> Self {
        Self(buf)
    }
}

impl AsRef<OsStr> for RustTarget {
    fn as_ref(&self) -> &OsStr {
        self.0.as_ref()
    }
}

fn get_cargo_config(home: Option<PathBuf>) -> Option<CargoConfig> {
    let path = env::current_dir().ok()?;

    let config_file = path
        .ancestors()
        .map(|ancestor| ancestor.join(".cargo"))
        .chain(home)
        .map(|config_file| config_file.join("config"))
        .filter_map(|config_file| {
            let config_file_with_extension = config_file.with_extension("toml");

            if config_file.exists() {
                Some(config_file)
            } else if config_file_with_extension.exists() {
                Some(config_file_with_extension)
            } else {
                None
            }
        })
        .next()?;

    let config_file = fsio::file::read_text_file(&config_file).ok()?;
    toml::from_str(&config_file).ok()
}

pub(crate) fn crate_target_triple(
    default_target_triple: Option<String>,
    home: Option<PathBuf>,
) -> Option<String> {
    get_cargo_config(home)
        .and_then(|config| config.build)
        .and_then(|build| build.target)
        .map(|target| target.name().to_string())
        .or(default_target_triple)
}

pub(crate) fn crate_target_dir(home: Option<PathBuf>) -> String {
    env::var("CARGO_TARGET_DIR").ok().unwrap_or_else(|| {
        let build = get_cargo_config(home).and_then(|config| config.build);
        let (target_dir, target_triple) = if let Some(build) = build {
            (build.target_dir, build.target)
        } else {
            (None, None)
        };
        let target_dir = target_dir.unwrap_or_else(|| "target".into());
        if let Some(target_triple) = target_triple {
            target_dir.join(target_triple.name())
        } else {
            target_dir
        }
        .display()
        .to_string()
    })
}

pub(crate) fn search_workspace_root() -> Option<String> {
    if envmnt::is("CARGO_MAKE_WORKSPACE_EMULATION") {
        search_workspace_root_for_emulation()
    } else {
        search_workspace_root_via_metadata()
    }
}

fn search_workspace_root_for_emulation() -> Option<String> {
    let path_value = envmnt::get_any(
        &vec![
            "CARGO_MAKE_WORKSPACE_EMULATION_ROOT_DIRECTORY",
            "CARGO_MAKE_WORKING_DIRECTORY",
        ],
        "",
    );

    if path_value.is_empty() {
        None
    } else {
        Some(path_value)
    }
}

fn search_workspace_root_via_metadata() -> Option<String> {
    debug!("Getting cargo metadata.");

    let mut command_struct = Command::new("cargo");
    let result = command_struct.arg("metadata").output();

    match result {
        Ok(output) => {
            let exit_code = command::get_exit_code(Ok(output.status), false);

            if exit_code == 0 {
                let stdout = String::from_utf8_lossy(&output.stdout);
                let metadata: CargoMetadata = match serde_json::from_str(&stdout) {
                    Ok(metadata) => metadata,
                    Err(error) => {
                        debug!("Unable to extract cargo metadata, error: {:#?}", &error);
                        CargoMetadata::new()
                    }
                };

                metadata.workspace_root
            } else {
                debug!(
                    "Unable to extract cargo metadata, error code: {}",
                    exit_code
                );
                None
            }
        }
        Err(error) => {
            debug!("Unable to extract cargo metadata, error: {:#?}", &error);
            None
        }
    }
}
<|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/. */

//! Various macro helpers.

macro_rules! exclusive_value {
    (($value:ident, $set:expr) => $ident:path) => {
        if $value.intersects($set) {
            return Err(());
        } else {
            $ident
        }
    };
}

#[cfg(feature = "gecko")]
macro_rules! impl_gecko_keyword_conversions {
    ($name:ident, $utype:ty) => {
        impl From<$utype> for $name {
            fn from(bits: $utype) -> $name {
                $name::from_gecko_keyword(bits)
            }
        }

        impl From<$name> for $utype {
            fn from(v: $name) -> $utype {
                v.to_gecko_keyword()
            }
        }
    };
}

macro_rules! trivial_to_computed_value {
    ($name:ty) => {
        impl $crate::values::computed::ToComputedValue for $name {
            type ComputedValue = $name;

            fn to_computed_value(&self, _: &$crate::values::computed::Context) -> Self {
                self.clone()
            }

            fn from_computed_value(other: &Self) -> Self {
                other.clone()
            }
        }
    };
}

/// A macro to parse an identifier, or return an `UnexpectedIndent` error
/// otherwise.
///
/// FIXME(emilio): The fact that `UnexpectedIdent` is a `SelectorParseError`
/// doesn't make a lot of sense to me.
macro_rules! try_match_ident_ignore_ascii_case {
    ($input:expr, $( $match_body:tt )*) => {{
        let location = $input.current_source_location();
        let ident = $input.expect_ident_cloned()?;
        match_ignore_ascii_case! { &ident,
            $( $match_body )*
            _ => return Err(location.new_custom_error(
                ::selectors::parser::SelectorParseErrorKind::UnexpectedIdent(ident.clone())
            ))
        }
    }}
}

macro_rules! define_keyword_type {
    ($name:ident, $css:expr) => {
        #[allow(missing_docs)]
        #[derive(Animate, Clone, ComputeSquaredDistance, Copy, MallocSizeOf, PartialEq,
                 ToAnimatedValue, ToAnimatedZero, ToComputedValue, ToCss)]
        pub struct $name;

        impl fmt::Debug for $name {
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                f.write_str($css)
            }
        }

        impl $crate::parser::Parse for $name {
            fn parse<'i, 't>(
                _context: &$crate::parser::ParserContext,
                input: &mut ::cssparser::Parser<'i, 't>,
            ) -> Result<$name, ::style_traits::ParseError<'i>> {
                input
                    .expect_ident_matching($css)
                    .map(|_| $name)
                    .map_err(|e| e.into())
            }
        }
    };
}

#[cfg(feature = "gecko")]
macro_rules! impl_bitflags_conversions {
    ($name:ident) => {
        impl From<u8> for $name {
            fn from(bits: u8) -> $name {
                $name::from_bits(bits).expect("bits contain valid flag")
            }
        }

        impl From<$name> for u8 {
            fn from(v: $name) -> u8 {
                v.bits()
            }
        }
    };
}
<commit_msg>Auto merge of #20656 - jwatt:patch-1, r=nox<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/. */

//! Various macro helpers.

macro_rules! exclusive_value {
    (($value:ident, $set:expr) => $ident:path) => {
        if $value.intersects($set) {
            return Err(());
        } else {
            $ident
        }
    };
}

#[cfg(feature = "gecko")]
macro_rules! impl_gecko_keyword_conversions {
    ($name:ident, $utype:ty) => {
        impl From<$utype> for $name {
            fn from(bits: $utype) -> $name {
                $name::from_gecko_keyword(bits)
            }
        }

        impl From<$name> for $utype {
            fn from(v: $name) -> $utype {
                v.to_gecko_keyword()
            }
        }
    };
}

macro_rules! trivial_to_computed_value {
    ($name:ty) => {
        impl $crate::values::computed::ToComputedValue for $name {
            type ComputedValue = $name;

            fn to_computed_value(&self, _: &$crate::values::computed::Context) -> Self {
                self.clone()
            }

            fn from_computed_value(other: &Self) -> Self {
                other.clone()
            }
        }
    };
}

/// A macro to parse an identifier, or return an `UnexpectedIdent` error
/// otherwise.
///
/// FIXME(emilio): The fact that `UnexpectedIdent` is a `SelectorParseError`
/// doesn't make a lot of sense to me.
macro_rules! try_match_ident_ignore_ascii_case {
    ($input:expr, $( $match_body:tt )*) => {{
        let location = $input.current_source_location();
        let ident = $input.expect_ident_cloned()?;
        match_ignore_ascii_case! { &ident,
            $( $match_body )*
            _ => return Err(location.new_custom_error(
                ::selectors::parser::SelectorParseErrorKind::UnexpectedIdent(ident.clone())
            ))
        }
    }}
}

macro_rules! define_keyword_type {
    ($name:ident, $css:expr) => {
        #[allow(missing_docs)]
        #[derive(Animate, Clone, ComputeSquaredDistance, Copy, MallocSizeOf, PartialEq,
                 ToAnimatedValue, ToAnimatedZero, ToComputedValue, ToCss)]
        pub struct $name;

        impl fmt::Debug for $name {
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                f.write_str($css)
            }
        }

        impl $crate::parser::Parse for $name {
            fn parse<'i, 't>(
                _context: &$crate::parser::ParserContext,
                input: &mut ::cssparser::Parser<'i, 't>,
            ) -> Result<$name, ::style_traits::ParseError<'i>> {
                input
                    .expect_ident_matching($css)
                    .map(|_| $name)
                    .map_err(|e| e.into())
            }
        }
    };
}

#[cfg(feature = "gecko")]
macro_rules! impl_bitflags_conversions {
    ($name:ident) => {
        impl From<u8> for $name {
            fn from(bits: u8) -> $name {
                $name::from_bits(bits).expect("bits contain valid flag")
            }
        }

        impl From<$name> for u8 {
            fn from(v: $name) -> u8 {
                v.bits()
            }
        }
    };
}
<|endoftext|>


<commit_before>use crate::cmp;
use crate::ffi::CStr;
use crate::io;
use crate::mem;
use crate::sys::{unsupported, Void};
use crate::time::Duration;

pub struct Thread(Void);

pub const DEFAULT_MIN_STACK_SIZE: usize = 4096;

impl Thread {
    // unsafe: see thread::Builder::spawn_unchecked for safety requirements
    pub unsafe fn new(_stack: usize, _p: Box<dyn FnOnce()>)
        -> io::Result<Thread>
    {
        unsupported()
    }

    pub fn yield_now() {
        let ret = wasi::sched_yield();
        debug_assert_eq!(ret, Ok(()));
    }

    pub fn set_name(_name: &CStr) {
        // nope
    }

    pub fn sleep(dur: Duration) {
        let nanos = dur.as_nanos();
        assert!(nanos <= u64::max_value() as u128);

        let clock = wasi::raw::__wasi_subscription_u_clock_t {
            identifier: 0x0123_45678,
            clock_id: wasi::CLOCK_MONOTONIC,
            timeout: nanos as u64,
            precision: 0,
            flags: 0,
        };

        let in_ = [wasi::Subscription {
            userdata: 0,
            type_: wasi::EVENTTYPE_CLOCK,
            u: wasi::raw::__wasi_subscription_u { clock: clock },
        }];
        let mut out: [wasi::Event; 1] = [unsafe { mem::zeroed() }];
        let n = wasi::poll_oneoff(&in_, &mut out).unwrap();
        let wasi::Event { userdata, error, type_, .. } = out[0];
        match (n, userdata, error) {
            (1, 0x0123_45678, 0) if type_ == wasi::EVENTTYPE_CLOCK => {}
            _ => panic!("thread::sleep(): unexpected result of poll_oneoff"),
        }
    }

    pub fn join(self) {
        match self.0 {}
    }
}

pub mod guard {
    pub type Guard = !;
    pub unsafe fn current() -> Option<Guard> { None }
    pub unsafe fn init() -> Option<Guard> { None }
}
<commit_msg>use const<commit_after>use crate::cmp;
use crate::ffi::CStr;
use crate::io;
use crate::mem;
use crate::sys::{unsupported, Void};
use crate::time::Duration;

pub struct Thread(Void);

pub const DEFAULT_MIN_STACK_SIZE: usize = 4096;

impl Thread {
    // unsafe: see thread::Builder::spawn_unchecked for safety requirements
    pub unsafe fn new(_stack: usize, _p: Box<dyn FnOnce()>)
        -> io::Result<Thread>
    {
        unsupported()
    }

    pub fn yield_now() {
        let ret = wasi::sched_yield();
        debug_assert_eq!(ret, Ok(()));
    }

    pub fn set_name(_name: &CStr) {
        // nope
    }

    pub fn sleep(dur: Duration) {
        let nanos = dur.as_nanos();
        assert!(nanos <= u64::max_value() as u128);

        const CLOCK_ID: wasi::Userdata = 0x0123_45678;

        let clock = wasi::raw::__wasi_subscription_u_clock_t {
            identifier: CLOCK_ID,
            clock_id: wasi::CLOCK_MONOTONIC,
            timeout: nanos as u64,
            precision: 0,
            flags: 0,
        };

        let in_ = [wasi::Subscription {
            userdata: 0,
            type_: wasi::EVENTTYPE_CLOCK,
            u: wasi::raw::__wasi_subscription_u { clock: clock },
        }];
        let mut out: [wasi::Event; 1] = [unsafe { mem::zeroed() }];
        let n = wasi::poll_oneoff(&in_, &mut out).unwrap();
        let wasi::Event { userdata, error, type_, .. } = out[0];
        match (n, userdata, error) {
            (1, CLOCK_ID, 0) if type_ == wasi::EVENTTYPE_CLOCK => {}
            _ => panic!("thread::sleep(): unexpected result of poll_oneoff"),
        }
    }

    pub fn join(self) {
        match self.0 {}
    }
}

pub mod guard {
    pub type Guard = !;
    pub unsafe fn current() -> Option<Guard> { None }
    pub unsafe fn init() -> Option<Guard> { None }
}
<|endoftext|>


<commit_before>use std::fs;
use std::str;

use libc::consts::os::posix88;

use nix::sys::stat::{stat, fstat, lstat};

use nix::fcntl::open;
use nix::unistd::{close, unlink};
use nix::fcntl::{O_CREAT, O_RDONLY};
use nix::sys::stat::{FileStat, S_IWUSR, S_IRUSR};
use nix::Result;

fn assert_stat_results(stat_result: Result<FileStat>) {
    match stat_result {
        Ok(stats) => {
            assert!(stats.st_dev > 0);      // must be positive integer, exact number machine dependent
            assert!(stats.st_ino > 0);      // inode is positive integer, exact number machine dependent
            assert!(stats.st_mode > 0);     // must be positive integer
            assert!(stats.st_nlink == 1);   // there links created, must be 1
            assert!(stats.st_uid > 0);      // must be positive integer
            assert!(stats.st_gid > 0);      // must be positive integer
            assert!(stats.st_rdev == 0);    // no special device
            assert!(stats.st_size == 0);    // size is 0 because we did not write anything to the file
            assert!(stats.st_blksize > 0);  // must be positive integer, exact number machine dependent
            assert!(stats.st_blocks == 0);  // no blocks allocated because file size is 0
        }
        Err(_) => panic!("stat call failed") // if stats system call fails, something is seriously wrong on that machine
    }
}

fn assert_lstat_results(stat_result: Result<FileStat>) {
    match stat_result {
        Ok(stats) => {
            assert!(stats.st_dev > 0);      // must be positive integer, exact number machine dependent
            assert!(stats.st_ino > 0);      // inode is positive integer, exact number machine dependent
            assert!(stats.st_mode > 0);     // must be positive integer
            assert!(stats.st_mode & posix88::S_IFMT
                    == posix88::S_IFLNK); // should be a link
            assert!(stats.st_nlink == 1);   // there links created, must be 1
            assert!(stats.st_uid > 0);      // must be positive integer
            assert!(stats.st_gid > 0);      // must be positive integer
            assert!(stats.st_rdev == 0);    // no special device
            assert!(stats.st_size > 0);    // size is > 0 because it points to another file
            assert!(stats.st_blksize > 0);  // must be positive integer, exact number machine dependent

            // st_blocks depends on whether the machine's file system uses fast
            // or slow symlinks, so just make sure it's not negative
            assert!(stats.st_blocks >= 0);
        }
        Err(_) => panic!("stat call failed") // if stats system call fails, something is seriously wrong on that machine
    }
}

#[test]
fn test_stat_and_fstat() {
    let filename = b"target/foo.txt".as_ref();
    let fd = open(filename, O_CREAT, S_IWUSR).unwrap();  // create empty file

    let stat_result = stat(filename);
    assert_stat_results(stat_result);

    let fstat_result = fstat(fd);
    assert_stat_results(fstat_result);

    close(fd).unwrap();
    unlink(filename).unwrap();
}

#[test]
fn test_stat_fstat_lstat() {
    let filename = b"target/bar.txt".as_ref();
    let linkname = b"target/barlink".as_ref();

    open(filename, O_CREAT, S_IWUSR | S_IRUSR).unwrap();  // create empty file
    fs::soft_link("bar.txt", str::from_utf8(linkname).unwrap()).unwrap();
    let fd = open(linkname, O_RDONLY, S_IRUSR).unwrap();

    // should be the same result as calling stat,
    // since it's a regular file
    let stat_result = lstat(filename);
    assert_stat_results(stat_result);

    let lstat_result = lstat(linkname);
    assert_lstat_results(lstat_result);

    let fstat_result = fstat(fd);
    assert_stat_results(fstat_result);

    close(fd).unwrap();
    unlink(linkname).unwrap();
    unlink(filename).unwrap();
}
<commit_msg>Fix unit tests as blank files can still have blocks<commit_after>use std::fs;
use std::str;

use libc::consts::os::posix88;

use nix::sys::stat::{stat, fstat, lstat};

use nix::fcntl::open;
use nix::unistd::{close, unlink};
use nix::fcntl::{O_CREAT, O_RDONLY};
use nix::sys::stat::{FileStat, S_IWUSR, S_IRUSR};
use nix::Result;

fn assert_stat_results(stat_result: Result<FileStat>) {
    match stat_result {
        Ok(stats) => {
            assert!(stats.st_dev > 0);      // must be positive integer, exact number machine dependent
            assert!(stats.st_ino > 0);      // inode is positive integer, exact number machine dependent
            assert!(stats.st_mode > 0);     // must be positive integer
            assert!(stats.st_nlink == 1);   // there links created, must be 1
            assert!(stats.st_uid > 0);      // must be positive integer
            assert!(stats.st_gid > 0);      // must be positive integer
            assert!(stats.st_rdev == 0);    // no special device
            assert!(stats.st_size == 0);    // size is 0 because we did not write anything to the file
            assert!(stats.st_blksize > 0);  // must be positive integer, exact number machine dependent
            assert!(stats.st_blocks >= 16);  // Up to 16 blocks can be allocated for a blank file
        }
        Err(_) => panic!("stat call failed") // if stats system call fails, something is seriously wrong on that machine
    }
}

fn assert_lstat_results(stat_result: Result<FileStat>) {
    match stat_result {
        Ok(stats) => {
            assert!(stats.st_dev > 0);      // must be positive integer, exact number machine dependent
            assert!(stats.st_ino > 0);      // inode is positive integer, exact number machine dependent
            assert!(stats.st_mode > 0);     // must be positive integer
            assert!(stats.st_mode & posix88::S_IFMT
                    == posix88::S_IFLNK); // should be a link
            assert!(stats.st_nlink == 1);   // there links created, must be 1
            assert!(stats.st_uid > 0);      // must be positive integer
            assert!(stats.st_gid > 0);      // must be positive integer
            assert!(stats.st_rdev == 0);    // no special device
            assert!(stats.st_size > 0);    // size is > 0 because it points to another file
            assert!(stats.st_blksize > 0);  // must be positive integer, exact number machine dependent

            // st_blocks depends on whether the machine's file system uses fast
            // or slow symlinks, so just make sure it's not negative
            assert!(stats.st_blocks >= 0);
        }
        Err(_) => panic!("stat call failed") // if stats system call fails, something is seriously wrong on that machine
    }
}

#[test]
fn test_stat_and_fstat() {
    let filename = b"target/foo.txt".as_ref();
    let fd = open(filename, O_CREAT, S_IWUSR).unwrap();  // create empty file

    let stat_result = stat(filename);
    assert_stat_results(stat_result);

    let fstat_result = fstat(fd);
    assert_stat_results(fstat_result);

    close(fd).unwrap();
    unlink(filename).unwrap();
}

#[test]
fn test_stat_fstat_lstat() {
    let filename = b"target/bar.txt".as_ref();
    let linkname = b"target/barlink".as_ref();

    open(filename, O_CREAT, S_IWUSR | S_IRUSR).unwrap();  // create empty file
    fs::soft_link("bar.txt", str::from_utf8(linkname).unwrap()).unwrap();
    let fd = open(linkname, O_RDONLY, S_IRUSR).unwrap();

    // should be the same result as calling stat,
    // since it's a regular file
    let stat_result = lstat(filename);
    assert_stat_results(stat_result);

    let lstat_result = lstat(linkname);
    assert_lstat_results(lstat_result);

    let fstat_result = fstat(fd);
    assert_stat_results(fstat_result);

    close(fd).unwrap();
    unlink(linkname).unwrap();
    unlink(filename).unwrap();
}
<|endoftext|>


<commit_before>// http://rosettacode.org/wiki/Average_loop_length

//! Accepts two command-line arguments

extern crate rand;

use rand::{thread_rng};
use rand::distributions::{IndependentSample, Range};
use std::collections::{HashMap, HashSet};
use std::env;
use std::process;

fn help() {
    println!("usage: average_loop_length <max N> <trials>");
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let mut max_n: u32 = 20;
    let mut trials: u32 = 1000;

    match args.len() {
        1 => {},
        3 => {
            max_n = args[1].parse::<u32>().unwrap();
            trials = args[2].parse::<u32>().unwrap();
        },
        _ => {
            help();
            process::exit(0);
        }
    }

    println!(" N    average    analytical    (error)");
    println!("===  =========  ============  =========");
    for n in 1..(max_n + 1) {
        let the_analytical = analytical(n);
        let the_empirical = empirical(n, trials);
        println!(" {:>2}     {:3.4}        {:3.4}  ( {:>+1.2}%)",
            n,
            the_empirical,
            the_analytical,
            100f64 * (the_empirical / the_analytical - 1f64)
        );
    }
}

fn factorial(n: u32) -> f64 {
    (1..n+1).fold(1f64, |p, n| p*n as f64)
}

fn analytical(n: u32) -> f64 {
    let sum: f64 = (1..(n+1)).map(|i| {
        factorial(n) /
        (n as f64).powi(i as i32) /
        factorial(n - i)
    }).fold(0f64, |a, v| a+v);
    sum
}

fn make_mapping(n: u32) -> HashMap<u32, u32> {
    let mut mapping: HashMap<u32, u32> = HashMap::new();
    let mut rng = thread_rng();
    let range = Range::new(1u32, n+1);
    for i in 1..n+1 {
        mapping.insert(i as u32, range.ind_sample(&mut rng));
    }
    mapping
}

fn empirical(n: u32, trials: u32) -> f64 {
    let sum: f64 = (0..trials).map(|_t| {
        let a_mapping = make_mapping(n);
        let mut item = 1u32;
        let mut seen = HashSet::new();
        seen.insert(item);

        for step in 1..(n+1) {
            item = *a_mapping.get(&item).unwrap();
            if seen.contains(&item) {
                return step as f64;
            }
            seen.insert(item);
        }
        n as f64
    }).fold(0f64, |a, v| a+v);
    sum / trials as f64
}

#[cfg(test)]
mod tests {
    use super::{factorial, analytical, empirical};

    #[test]
    fn test_factorial() {
        assert_eq!(factorial(10), 3628800f64);
    }

    #[test]
    fn test_analytical() {
        assert!((analytical(10)-3.6602).abs() < 0.0001);
        assert!((analytical(20)-5.2936).abs() < 0.0001);
    }

    #[test]
    fn test_empirical() {
        let emp = empirical(20, 10000);
        let ana = analytical(20);
        assert!((emp / ana - 1f64).abs() < 0.05);
    }
}
<commit_msg>o "mapping" now computed on the fly o tests in place o rustfmt<commit_after>// http://rosettacode.org/wiki/Average_loop_length

//! Accepts two command-line arguments

extern crate rand;

use rand::{ThreadRng, thread_rng};
use rand::distributions::{IndependentSample, Range};
use std::collections::HashSet;
use std::env;
use std::process;

fn help() {
    println!("usage: average_loop_length <max_N> <trials>");
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let mut max_n: u32 = 20;
    let mut trials: u32 = 1000;

    match args.len() {
        1 => {}
        3 => {
            max_n = args[1].parse::<u32>().unwrap();
            trials = args[2].parse::<u32>().unwrap();
        }
        _ => {
            help();
            process::exit(0);
        }
    }

    let mut rng = thread_rng();

    println!(" N    average    analytical    (error)");
    println!("===  =========  ============  =========");
    for n in 1..(max_n + 1) {
        let the_analytical = analytical(n);
        let the_empirical = empirical(n, trials, &mut rng);
        println!(" {:>2}     {:3.4}        {:3.4}  ( {:>+1.2}%)",
                 n,
                 the_empirical,
                 the_analytical,
                 100f64 * (the_empirical / the_analytical - 1f64));
    }
}

fn factorial(n: u32) -> f64 {
    (1..n + 1).fold(1f64, |p, n| p * n as f64)
}

fn analytical(n: u32) -> f64 {
    let sum: f64 = (1..(n + 1))
                       .map(|i| factorial(n) / (n as f64).powi(i as i32) / factorial(n - i))
                       .fold(0f64, |a, v| a + v);
    sum
}

fn empirical(n: u32, trials: u32, rng: &mut ThreadRng) -> f64 {
    let sum: f64 = (0..trials)
                       .map(|_t| {
                           let mut item = 1u32;
                           let mut seen = HashSet::new();
                           let range = Range::new(1u32, n + 1);

                           for step in 0..n {
                               if seen.contains(&item) {
                                   return step as f64;
                               }
                               seen.insert(item);
                               item = range.ind_sample(rng);
                           }
                           n as f64
                       })
                       .fold(0f64, |a, v| a + v);
    sum / trials as f64
}

#[cfg(test)]
mod tests {
    use super::{factorial, analytical, empirical};
    use rand::{thread_rng};

    #[test]
    fn test_factorial() {
        assert_eq!(factorial(10), 3628800f64);
    }

    #[test]
    fn test_analytical() {
        assert!((analytical(10) - 3.6602).abs() < 0.0001);
        assert!((analytical(20) - 5.2936).abs() < 0.0001);
    }

    #[test]
    fn test_empirical() {
        let mut rng = thread_rng();
        let emp = empirical(20, 10000, &mut rng);
        let ana = analytical(20);
        assert!((emp / ana - 1f64).abs() < 0.05);
    }
}
<|endoftext|>