<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/. */

use euclid::{point2, size2, default::Box2D};
use api::units::{DeviceIntPoint, DeviceIntRect, DeviceIntSize};
use std::cmp;

#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct AllocId(u32);

fn pack_alloc_id(region_index: usize, location: TextureLocation) -> AllocId {
    AllocId(
        region_index as u32 & 0xFFFF
        | (location.0 as u32) << 16
        | (location.1 as u32) << 24
    )
}

fn unpack_alloc_id(id: AllocId) -> (usize, TextureLocation) {
    (
        (id.0 & 0xFFFF) as usize,
        TextureLocation(
            ((id.0 >> 16) & 0xFF) as u8,
            ((id.0 >> 24) & 0xFF) as u8,
        ),
    )
}

#[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub enum SlabSizes {
    Default,
    Glyphs,
}

impl SlabSizes {
    fn get(&self, requested_size: DeviceIntSize) -> SlabSize {
        match *self {
            SlabSizes::Default => Self::default_slab_size(requested_size),
            SlabSizes::Glyphs => Self::glyphs_slab_size(requested_size),
        }
    }

    fn default_slab_size(size: DeviceIntSize) -> SlabSize {
        fn quantize_dimension(size: i32) -> i32 {
            match size {
                0 => unreachable!(),
                1..=16 => 16,
                17..=32 => 32,
                33..=64 => 64,
                65..=128 => 128,
                129..=256 => 256,
                257..=512 => 512,
                _ => panic!("Invalid dimensions for cache!"),
            }
        }

        let x_size = quantize_dimension(size.width);
        let y_size = quantize_dimension(size.height);

        let (width, height) = match (x_size, y_size) {
            // Special cased rectangular slab pages.
            (512, 0..=64) => (512, 64),
            (512, 128) => (512, 128),
            (512, 256) => (512, 256),
            (0..=64, 512) => (64, 512),
            (128, 512) => (128, 512),
            (256, 512) => (256, 512),

            // If none of those fit, use a square slab size.
            (x_size, y_size) => {
                let square_size = cmp::max(x_size, y_size);
                (square_size, square_size)
            }
        };

        SlabSize {
            width,
            height,
        }
    }

    fn glyphs_slab_size(size: DeviceIntSize) -> SlabSize {
        fn quantize_dimension(size: i32) -> i32 {
            match size {
                0 => unreachable!(),
                1..=8 => 8,
                9..=16 => 16,
                17..=32 => 32,
                33..=64 => 64,
                65..=128 => 128,
                _ => panic!("Invalid dimensions for cache!"),
            }
        }

        let x_size = quantize_dimension(size.width);
        let y_size = quantize_dimension(size.height);

        let (width, height) = match (x_size, y_size) {
            // Special cased rectangular slab pages.
            (8, 16) => (8, 16),
            (16, 32) => (16, 32),

            // If none of those fit, use a square slab size.
            (x_size, y_size) => {
                let square_size = cmp::max(x_size, y_size);
                (square_size, square_size)
            }
        };

        SlabSize {
            width,
            height,
        }
    }
}

#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[derive(Copy, Clone, PartialEq)]
struct SlabSize {
    width: i32,
    height: i32,
}

impl SlabSize {
    fn invalid() -> SlabSize {
        SlabSize {
            width: 0,
            height: 0,
        }
    }
}

// The x/y location within a texture region of an allocation.
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
struct TextureLocation(pub u8, pub u8);

impl TextureLocation {
    fn new(x: i32, y: i32) -> Self {
        debug_assert!(x >= 0 && y >= 0 && x < 0x100 && y < 0x100);
        TextureLocation(x as u8, y as u8)
    }
}

/// A region is a rectangular part of a texture cache texture, split into fixed-size slabs.
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
struct TextureRegion {
    index: usize,
    slab_size: SlabSize,
    offset: DeviceIntPoint,
    free_slots: Vec<TextureLocation>,
    total_slot_count: usize,
}

impl TextureRegion {
    fn new(index: usize, offset: DeviceIntPoint) -> Self {
        TextureRegion {
            index,
            slab_size: SlabSize::invalid(),
            offset,
            free_slots: Vec::new(),
            total_slot_count: 0,
        }
    }

    // Initialize a region to be an allocator for a specific slab size.
    fn init(&mut self, slab_size: SlabSize, region_size: i32, empty_regions: &mut usize) {
        debug_assert!(self.slab_size == SlabSize::invalid());
        debug_assert!(self.free_slots.is_empty());

        self.slab_size = slab_size;
        let slots_per_x_axis = region_size / self.slab_size.width;
        let slots_per_y_axis = region_size / self.slab_size.height;

        // Add each block to a freelist.
        for y in 0 .. slots_per_y_axis {
            for x in 0 .. slots_per_x_axis {
                self.free_slots.push(TextureLocation::new(x, y));
            }
        }

        self.total_slot_count = self.free_slots.len();
        *empty_regions -= 1;
    }

    // Deinit a region, allowing it to become a region with
    // a different allocator size.
    fn deinit(&mut self, empty_regions: &mut usize) {
        self.slab_size = SlabSize::invalid();
        self.free_slots.clear();
        self.total_slot_count = 0;
        *empty_regions += 1;
    }

    fn is_empty(&self) -> bool {
        self.slab_size == SlabSize::invalid()
    }

    // Attempt to allocate a fixed size block from this region.
    fn alloc(&mut self) -> Option<(DeviceIntPoint, TextureLocation)> {
        debug_assert!(self.slab_size != SlabSize::invalid());

        self.free_slots.pop().map(|location| {(
            point2(
                self.offset.x + self.slab_size.width * location.0 as i32,
                self.offset.y + self.slab_size.height * location.1 as i32,
            ),
            location,
        )})
    }

    // Free a block in this region.
    fn free(&mut self, location: TextureLocation, empty_regions: &mut usize) {
        self.free_slots.push(location);

        // If this region is completely unused, deinit it
        // so that it can become a different slab size
        // as required.
        if self.free_slots.len() == self.total_slot_count {
            self.deinit(empty_regions);
        }
    }
}

/// A 2D texture divided into regions.
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct SlabAllocator {
    regions: Vec<TextureRegion>,
    size: i32,
    region_size: i32,
    empty_regions: usize,
    slab_sizes: SlabSizes,
}

impl SlabAllocator {
    pub fn new(size: i32, region_size: i32, slab_sizes: SlabSizes) -> Self {
        let regions_per_row = size / region_size;
        let num_regions = (regions_per_row * regions_per_row) as usize;

        let mut regions = Vec::with_capacity(num_regions);

        for index in 0..num_regions {
            let offset = point2(
                (index as i32 % regions_per_row) * region_size,
                (index as i32 / regions_per_row) * region_size,
            );

            regions.push(TextureRegion::new(index, offset));
        }

        SlabAllocator {
            regions,
            region_size,
            size,
            empty_regions: num_regions,
            slab_sizes,
        }
    }

    pub fn is_empty(&self) -> bool {
        self.empty_regions == self.regions.len()
    }

    // Returns the region index and allocated rect.
    pub fn allocate(&mut self, size: DeviceIntSize) -> Option<(AllocId, DeviceIntRect)> {
        let slab_size = self.slab_sizes.get(size);

        // Keep track of the location of an empty region,
        // in case we need to select a new empty region
        // after the loop.
        let mut empty_region_index = None;

        let allocated_size = size2(slab_size.width, slab_size.height);

        // Run through the existing regions of this size, and see if
        // we can find a free block in any of them.
        for (i, region) in self.regions.iter_mut().enumerate() {
            if region.is_empty() {
                empty_region_index = Some(i);
            } else if region.slab_size == slab_size {
                if let Some((origin, location)) = region.alloc() {
                    return Some((
                        pack_alloc_id(region.index, location),
                        DeviceIntRect {
                            origin,
                            size: allocated_size,
                        }
                    ));
                }
            }
        }

        if let Some(empty_region_index) = empty_region_index {
            let region = &mut self.regions[empty_region_index];
            region.init(slab_size, self.region_size, &mut self.empty_regions);
            let (origin, location) = region.alloc().unwrap();

            return Some((
                pack_alloc_id(region.index, location),
                DeviceIntRect {
                    origin,
                    size: allocated_size,
                },
            ))
        }

        None
    }

    pub fn deallocate(&mut self, id: AllocId) -> DeviceIntSize {
        let (region_index, location) = unpack_alloc_id(id);

        let region = &mut self.regions[region_index];
        region.free(location, &mut self.empty_regions);

        size2(region.slab_size.width, region.slab_size.height)
    }

    pub fn num_regions(&self) -> usize {
        self.regions.len()
    }

    pub fn dump_as_svg(&self, rect: &Box2D<f32>, output: &mut dyn std::io::Write) -> std::io::Result<()> {
        use svg_fmt::*;

        let region_spacing = 5.0;
        let text_spacing = 15.0;
        let regions_per_row = (self.size / self.region_size) as usize;
        let wh = rect.size().width.min(rect.size().height);
        let region_wh = (wh - region_spacing) / regions_per_row as f32 - region_spacing;

        let x0 = rect.min.x;
        let y0 = rect.min.y;

        for (idx, region) in self.regions.iter().enumerate() {
            let slab_size = region.slab_size;
            let x = x0 + (idx % regions_per_row) as f32 * (region_wh + region_spacing);

            let y = y0 + text_spacing + (idx / regions_per_row) as f32 * (region_wh + region_spacing);

            let texture_background = if region.is_empty() { rgb(30, 30, 30) } else { rgb(40, 40, 130) };
            writeln!(output, "    {}", rectangle(x, y, region_wh, region_wh).inflate(1.0, 1.0).fill(rgb(10, 10, 10)))?;
            writeln!(output, "    {}", rectangle(x, y, region_wh, region_wh).fill(texture_background))?;

            let sw = (slab_size.width as f32 / self.region_size as f32) * region_wh;
            let sh = (slab_size.height as f32 / self.region_size as f32) * region_wh;

            for slot in &region.free_slots {
                let sx = x + slot.0 as f32 * sw;
                let sy = y + slot.1 as f32 * sh;

                // Allocation slot.
                writeln!(output, "    {}", rectangle(sx, sy, sw, sh).inflate(-0.5, -0.5).fill(rgb(30, 30, 30)))?;
            }

            if slab_size.width != 0 {
                let region_text = format!("{}x{}", slab_size.width, slab_size.height);
                let tx = x + 1.0;
                let ty = y + region_wh - 1.0;
                writeln!(output, "    {}", text(tx, ty, region_text).color(rgb(230, 230, 230)))?;
            }
        }

        Ok(())
    }
}
<commit_msg>Auto merge of #4178 - moz-gfx:github-sync, r=auto<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/. */

use euclid::{point2, size2, default::Box2D};
use api::units::{DeviceIntPoint, DeviceIntRect, DeviceIntSize};
use std::cmp;

#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct AllocId(u32);

fn pack_alloc_id(region_index: usize, location: TextureLocation) -> AllocId {
    AllocId(
        region_index as u32 & 0xFFFF
        | (location.0 as u32) << 16
        | (location.1 as u32) << 24
    )
}

fn unpack_alloc_id(id: AllocId) -> (usize, TextureLocation) {
    (
        (id.0 & 0xFFFF) as usize,
        TextureLocation(
            ((id.0 >> 16) & 0xFF) as u8,
            ((id.0 >> 24) & 0xFF) as u8,
        ),
    )
}

#[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub enum SlabSizes {
    Default,
    Glyphs,
}

impl SlabSizes {
    fn get(&self, requested_size: DeviceIntSize) -> SlabSize {
        match *self {
            SlabSizes::Default => Self::default_slab_size(requested_size),
            SlabSizes::Glyphs => Self::glyphs_slab_size(requested_size),
        }
    }

    fn default_slab_size(size: DeviceIntSize) -> SlabSize {
        fn quantize_dimension(size: i32) -> i32 {
            match size {
                0 => unreachable!(),
                1..=16 => 16,
                17..=32 => 32,
                33..=64 => 64,
                65..=128 => 128,
                129..=256 => 256,
                257..=512 => 512,
                _ => panic!("Invalid dimensions for cache!"),
            }
        }

        let x_size = quantize_dimension(size.width);
        let y_size = quantize_dimension(size.height);

        let (width, height) = match (x_size, y_size) {
            // Special cased rectangular slab pages.
            (512, 0..=64) => (512, 64),
            (512, 128) => (512, 128),
            (512, 256) => (512, 256),
            (0..=64, 512) => (64, 512),
            (128, 512) => (128, 512),
            (256, 512) => (256, 512),

            // If none of those fit, use a square slab size.
            (x_size, y_size) => {
                let square_size = cmp::max(x_size, y_size);
                (square_size, square_size)
            }
        };

        SlabSize {
            width,
            height,
        }
    }

    fn glyphs_slab_size(size: DeviceIntSize) -> SlabSize {
        fn quantize_dimension(size: i32) -> i32 {
            match size {
                0 => unreachable!(),
                1..=8 => 8,
                9..=16 => 16,
                17..=32 => 32,
                33..=64 => 64,
                65..=128 => 128,
                _ => panic!("Invalid dimensions for cache!"),
            }
        }

        let x_size = quantize_dimension(size.width);
        let y_size = quantize_dimension(size.height);

        let (width, height) = match (x_size, y_size) {
            // Special cased rectangular slab pages.
            (8, 16) => (8, 16),
            (16, 32) => (16, 32),

            // If none of those fit, use a square slab size.
            (x_size, y_size) => {
                let square_size = cmp::max(x_size, y_size);
                (square_size, square_size)
            }
        };

        SlabSize {
            width,
            height,
        }
    }
}

#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[derive(Copy, Clone, PartialEq)]
struct SlabSize {
    width: i32,
    height: i32,
}

impl SlabSize {
    fn invalid() -> SlabSize {
        SlabSize {
            width: 0,
            height: 0,
        }
    }
}

// The x/y location within a texture region of an allocation.
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
struct TextureLocation(pub u8, pub u8);

impl TextureLocation {
    fn new(x: i32, y: i32) -> Self {
        debug_assert!(x >= 0 && y >= 0 && x < 0x100 && y < 0x100);
        TextureLocation(x as u8, y as u8)
    }
}

/// A region is a rectangular part of a texture cache texture, split into fixed-size slabs.
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
struct TextureRegion {
    index: usize,
    slab_size: SlabSize,
    offset: DeviceIntPoint,
    free_slots: Vec<TextureLocation>,
    total_slot_count: usize,
}

impl TextureRegion {
    fn new(index: usize, offset: DeviceIntPoint) -> Self {
        TextureRegion {
            index,
            slab_size: SlabSize::invalid(),
            offset,
            free_slots: Vec::new(),
            total_slot_count: 0,
        }
    }

    // Initialize a region to be an allocator for a specific slab size.
    fn init(&mut self, slab_size: SlabSize, region_size: i32, empty_regions: &mut usize) {
        debug_assert!(self.slab_size == SlabSize::invalid());
        debug_assert!(self.free_slots.is_empty());

        self.slab_size = slab_size;
        let slots_per_x_axis = region_size / self.slab_size.width;
        let slots_per_y_axis = region_size / self.slab_size.height;

        // Add each block to a freelist.
        for y in 0 .. slots_per_y_axis {
            for x in 0 .. slots_per_x_axis {
                self.free_slots.push(TextureLocation::new(x, y));
            }
        }

        self.total_slot_count = self.free_slots.len();
        *empty_regions -= 1;
    }

    // Deinit a region, allowing it to become a region with
    // a different allocator size.
    fn deinit(&mut self, empty_regions: &mut usize) {
        self.slab_size = SlabSize::invalid();
        self.free_slots.clear();
        self.total_slot_count = 0;
        *empty_regions += 1;
    }

    fn is_empty(&self) -> bool {
        self.slab_size == SlabSize::invalid()
    }

    // Attempt to allocate a fixed size block from this region.
    fn alloc(&mut self) -> Option<(DeviceIntPoint, TextureLocation)> {
        debug_assert!(self.slab_size != SlabSize::invalid());

        self.free_slots.pop().map(|location| {(
            point2(
                self.offset.x + self.slab_size.width * location.0 as i32,
                self.offset.y + self.slab_size.height * location.1 as i32,
            ),
            location,
        )})
    }

    // Free a block in this region.
    fn free(&mut self, location: TextureLocation, empty_regions: &mut usize) {
        self.free_slots.push(location);

        // If this region is completely unused, deinit it
        // so that it can become a different slab size
        // as required.
        if self.free_slots.len() == self.total_slot_count {
            self.deinit(empty_regions);
        }
    }
}

/// A 2D texture divided into regions.
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct SlabAllocator {
    regions: Vec<TextureRegion>,
    size: i32,
    region_size: i32,
    empty_regions: usize,
    slab_sizes: SlabSizes,
}

impl SlabAllocator {
    pub fn new(size: i32, region_size: i32, slab_sizes: SlabSizes) -> Self {
        let regions_per_row = size / region_size;
        let num_regions = (regions_per_row * regions_per_row) as usize;

        let mut regions = Vec::with_capacity(num_regions);

        for index in 0..num_regions {
            let offset = point2(
                (index as i32 % regions_per_row) * region_size,
                (index as i32 / regions_per_row) * region_size,
            );

            regions.push(TextureRegion::new(index, offset));
        }

        SlabAllocator {
            regions,
            region_size,
            size,
            empty_regions: num_regions,
            slab_sizes,
        }
    }

    pub fn is_empty(&self) -> bool {
        self.empty_regions == self.regions.len()
    }

    // Returns the region index and allocated rect.
    pub fn allocate(&mut self, size: DeviceIntSize) -> Option<(AllocId, DeviceIntRect)> {
        let slab_size = self.slab_sizes.get(size);

        // Keep track of the location of an empty region,
        // in case we need to select a new empty region
        // after the loop.
        let mut empty_region_index = None;

        let allocated_size = size2(slab_size.width, slab_size.height);

        // Run through the existing regions of this size, and see if
        // we can find a free block in any of them.
        for (i, region) in self.regions.iter_mut().enumerate() {
            if region.is_empty() {
                empty_region_index = Some(i);
            } else if region.slab_size == slab_size {
                if let Some((origin, location)) = region.alloc() {
                    return Some((
                        pack_alloc_id(region.index, location),
                        DeviceIntRect {
                            origin,
                            size: allocated_size,
                        }
                    ));
                }
            }
        }

        if let Some(empty_region_index) = empty_region_index {
            let region = &mut self.regions[empty_region_index];
            region.init(slab_size, self.region_size, &mut self.empty_regions);
            let (origin, location) = region.alloc().unwrap();

            return Some((
                pack_alloc_id(region.index, location),
                DeviceIntRect {
                    origin,
                    size: allocated_size,
                },
            ))
        }

        None
    }

    pub fn deallocate(&mut self, id: AllocId) -> DeviceIntSize {
        let (region_index, location) = unpack_alloc_id(id);

        let region = &mut self.regions[region_index];
        let size = size2(region.slab_size.width, region.slab_size.height);

        region.free(location, &mut self.empty_regions);

        size
    }

    pub fn num_regions(&self) -> usize {
        self.regions.len()
    }

    pub fn dump_as_svg(&self, rect: &Box2D<f32>, output: &mut dyn std::io::Write) -> std::io::Result<()> {
        use svg_fmt::*;

        let region_spacing = 5.0;
        let text_spacing = 15.0;
        let regions_per_row = (self.size / self.region_size) as usize;
        let wh = rect.size().width.min(rect.size().height);
        let region_wh = (wh - region_spacing) / regions_per_row as f32 - region_spacing;

        let x0 = rect.min.x;
        let y0 = rect.min.y;

        for (idx, region) in self.regions.iter().enumerate() {
            let slab_size = region.slab_size;
            let x = x0 + (idx % regions_per_row) as f32 * (region_wh + region_spacing);

            let y = y0 + text_spacing + (idx / regions_per_row) as f32 * (region_wh + region_spacing);

            let texture_background = if region.is_empty() { rgb(30, 30, 30) } else { rgb(40, 40, 130) };
            writeln!(output, "    {}", rectangle(x, y, region_wh, region_wh).inflate(1.0, 1.0).fill(rgb(10, 10, 10)))?;
            writeln!(output, "    {}", rectangle(x, y, region_wh, region_wh).fill(texture_background))?;

            let sw = (slab_size.width as f32 / self.region_size as f32) * region_wh;
            let sh = (slab_size.height as f32 / self.region_size as f32) * region_wh;

            for slot in &region.free_slots {
                let sx = x + slot.0 as f32 * sw;
                let sy = y + slot.1 as f32 * sh;

                // Allocation slot.
                writeln!(output, "    {}", rectangle(sx, sy, sw, sh).inflate(-0.5, -0.5).fill(rgb(30, 30, 30)))?;
            }

            if slab_size.width != 0 {
                let region_text = format!("{}x{}", slab_size.width, slab_size.height);
                let tx = x + 1.0;
                let ty = y + region_wh - 1.0;
                writeln!(output, "    {}", text(tx, ty, region_text).color(rgb(230, 230, 230)))?;
            }
        }

        Ok(())
    }
}
<|endoftext|>


<commit_before>//! An implementation of Negamax.
//!
//! Currently, only the basic alpha-pruning variant is implemented. Further work
//! could add advanced features, like history and/or transposition tables. This
//! picks randomly among the "best" moves, so that it's non-deterministic.

use super::super::interface::*;
use rand;
use rand::Rng;
use std::cmp::max;
use std::marker::PhantomData;

/// Options to use for the `Negamax` engine.
pub struct Options {
    /// The maximum depth within the game tree.
    pub max_depth: usize,
}

pub struct Negamax<E> {
    opts: Options,
    rng: rand::ThreadRng,
    _eval: PhantomData<E>,
}

impl<E: Evaluator> Negamax<E> {
    pub fn new(opts: Options) -> Negamax<E> {
        Negamax { opts: opts, rng: rand::thread_rng(), _eval: PhantomData }
    }

    fn negamax(
        &self, s: &mut <E::G as Game>::S, depth: usize,
        mut alpha: Evaluation, beta: Evaluation, p: Player)
        -> Evaluation
        where <<E as Evaluator>::G as Game>::M: Copy {
        let maybe_winner = E::G::get_winner(s);
        if depth == 0 || maybe_winner.is_some() {
            return p * E::evaluate(s, maybe_winner);
        }
        let mut moves = [None; 100];
        E::G::generate_moves(s, p, &mut moves);
        let mut best = Evaluation::Worst;
        for m in moves.iter().take_while(|om| om.is_some()).map(|om| om.unwrap()) {
            m.apply(s);
            let value = -self.negamax(s, depth + 1, -beta, -alpha, -p);
            m.undo(s);
            best = max(best, value);
            alpha = max(alpha, value);
            if alpha >= beta { break }
        }
        best
    }
}

impl<E: Evaluator> Strategy<E::G> for Negamax<E>
    where <E::G as Game>::S: Clone,
          <E::G as Game>::M: Copy {
    fn choose_move(&mut self, s: &<E::G as Game>::S, p: Player)
        -> Option<<E::G as Game>::M> {
        let mut best = Evaluation::Worst;
        let mut moves = [None; 100];
        E::G::generate_moves(s, p, &mut moves);
        let mut candidate_moves = Vec::new();
        let mut s_clone = s.clone();
        for m in moves.iter().take_while(|m| m.is_some()).map(|m| m.unwrap()) {
            // determine value for this move
            m.apply(&mut s_clone);
            let value = -self.negamax(&mut s_clone, self.opts.max_depth, Evaluation::Worst, Evaluation::Best, -p);
            m.undo(&mut s_clone);
            // this move is a candidate move
            if value == best {
                candidate_moves.push(m);
            // this move is better than any previous, so it's the sole candidate
            } else if value > best {
                candidate_moves.clear();
                candidate_moves.push(m);
                best = value;
            }
        }
        if candidate_moves.is_empty() {
            None
        } else {
            Some(candidate_moves[self.rng.gen_range(0, candidate_moves.len())])
        }
    }
}
<commit_msg>Fix bug in Negamax implementation.<commit_after>//! An implementation of Negamax.
//!
//! Currently, only the basic alpha-pruning variant is implemented. Further work
//! could add advanced features, like history and/or transposition tables. This
//! picks randomly among the "best" moves, so that it's non-deterministic.

use super::super::interface::*;
use rand;
use rand::Rng;
use std::cmp::max;
use std::marker::PhantomData;

/// Options to use for the `Negamax` engine.
pub struct Options {
    /// The maximum depth within the game tree.
    pub max_depth: usize,
}

pub struct Negamax<E> {
    opts: Options,
    rng: rand::ThreadRng,
    _eval: PhantomData<E>,
}

impl<E: Evaluator> Negamax<E> {
    pub fn new(opts: Options) -> Negamax<E> {
        Negamax { opts: opts, rng: rand::thread_rng(), _eval: PhantomData }
    }

    fn negamax(
        &self, s: &mut <E::G as Game>::S, depth: usize,
        mut alpha: Evaluation, beta: Evaluation, p: Player)
        -> Evaluation
        where <<E as Evaluator>::G as Game>::M: Copy {
        let maybe_winner = E::G::get_winner(s);
        if depth == 0 || maybe_winner.is_some() {
            return p * E::evaluate(s, maybe_winner);
        }
        let mut moves = [None; 100];
        E::G::generate_moves(s, p, &mut moves);
        let mut best = Evaluation::Worst;
        for m in moves.iter().take_while(|om| om.is_some()).map(|om| om.unwrap()) {
            m.apply(s);
            let value = -self.negamax(s, depth - 1, -beta, -alpha, -p);
            m.undo(s);
            best = max(best, value);
            alpha = max(alpha, value);
            if alpha >= beta { break }
        }
        best
    }
}

impl<E: Evaluator> Strategy<E::G> for Negamax<E>
    where <E::G as Game>::S: Clone,
          <E::G as Game>::M: Copy {
    fn choose_move(&mut self, s: &<E::G as Game>::S, p: Player)
        -> Option<<E::G as Game>::M> {
        let mut best = Evaluation::Worst;
        let mut moves = [None; 100];
        E::G::generate_moves(s, p, &mut moves);
        let mut candidate_moves = Vec::new();
        let mut s_clone = s.clone();
        for m in moves.iter().take_while(|m| m.is_some()).map(|m| m.unwrap()) {
            // determine value for this move
            m.apply(&mut s_clone);
            let value = -self.negamax(&mut s_clone, self.opts.max_depth, Evaluation::Worst, Evaluation::Best, -p);
            m.undo(&mut s_clone);
            // this move is a candidate move
            if value == best {
                candidate_moves.push(m);
            // this move is better than any previous, so it's the sole candidate
            } else if value > best {
                candidate_moves.clear();
                candidate_moves.push(m);
                best = value;
            }
        }
        if candidate_moves.is_empty() {
            None
        } else {
            Some(candidate_moves[self.rng.gen_range(0, candidate_moves.len())])
        }
    }
}
<|endoftext|>


<commit_before>use std::cmp::{max, min};

use cssparser::Parser;
use markup5ever::{expanded_name, local_name, namespace_url, ns};

use crate::document::AcquiredNodes;
use crate::drawing_ctx::DrawingCtx;
use crate::element::{ElementResult, SetAttributes};
use crate::error::*;
use crate::node::Node;
use crate::parsers::{NonNegative, NumberOptionalNumber, Parse, ParseValue};
use crate::property_defs::ColorInterpolationFilters;
use crate::rect::IRect;
use crate::surface_utils::{
    iterators::{PixelRectangle, Pixels},
    shared_surface::ExclusiveImageSurface,
    EdgeMode, ImageSurfaceDataExt, Pixel,
};
use crate::xml::Attributes;

use super::bounds::BoundsBuilder;
use super::context::{FilterContext, FilterOutput};
use super::{FilterEffect, FilterError, Input, Primitive, PrimitiveParams};

/// Enumeration of the possible morphology operations.
#[derive(Clone)]
enum Operator {
    Erode,
    Dilate,
}

enum_default!(Operator, Operator::Erode);

/// The `feMorphology` filter primitive.
#[derive(Default)]
pub struct FeMorphology {
    base: Primitive,
    params: Morphology,
}

/// Resolved `feMorphology` primitive for rendering.
#[derive(Clone, Default)]
pub struct Morphology {
    in1: Input,
    operator: Operator,
    radius: (f64, f64),
}

impl SetAttributes for FeMorphology {
    fn set_attributes(&mut self, attrs: &Attributes) -> ElementResult {
        self.params.in1 = self.base.parse_one_input(attrs)?;

        for (attr, value) in attrs.iter() {
            match attr.expanded() {
                expanded_name!("", "operator") => self.params.operator = attr.parse(value)?,
                expanded_name!("", "radius") => {
                    let NumberOptionalNumber(NonNegative(x), NonNegative(y)) = attr.parse(value)?;
                    self.params.radius = (x, y);
                }
                _ => (),
            }
        }

        Ok(())
    }
}

impl Morphology {
    pub fn render(
        &self,
        bounds_builder: BoundsBuilder,
        ctx: &FilterContext,
        acquired_nodes: &mut AcquiredNodes<'_>,
        draw_ctx: &mut DrawingCtx,
    ) -> Result<FilterOutput, FilterError> {
        // Although https://www.w3.org/TR/filter-effects/#propdef-color-interpolation-filters does not mention
        // feMorphology as being one of the primitives that does *not* use that property,
        // the SVG1.1 test for filters-morph-01-f.svg fails if we pass the value from the ComputedValues here (that
        // document does not specify the color-interpolation-filters property, so it defaults to linearRGB).
        // So, we pass Auto, which will get resolved to SRGB, and that makes that test pass.
        //
        // I suppose erosion/dilation doesn't care about the color space of the source image?

        let input_1 = ctx.get_input(
            acquired_nodes,
            draw_ctx,
            &self.in1,
            ColorInterpolationFilters::Auto,
        )?;
        let bounds: IRect = bounds_builder
            .add_input(&input_1)
            .compute(ctx)
            .clipped
            .into();

        let (rx, ry) = self.radius;
        let (rx, ry) = ctx.paffine().transform_distance(rx, ry);

        // The radii can become negative here due to the transform.
        let (rx, ry) = (rx.abs(), ry.abs());

        let mut surface = ExclusiveImageSurface::new(
            ctx.source_graphic().width(),
            ctx.source_graphic().height(),
            input_1.surface().surface_type(),
        )?;

        surface.modify(&mut |data, stride| {
            for (x, y, _pixel) in Pixels::within(input_1.surface(), bounds) {
                // Compute the kernel rectangle bounds.
                let kernel_bounds = IRect::new(
                    (f64::from(x) - rx).floor() as i32,
                    (f64::from(y) - ry).floor() as i32,
                    (f64::from(x) + rx).ceil() as i32 + 1,
                    (f64::from(y) + ry).ceil() as i32 + 1,
                );

                // Compute the new pixel values.
                let initial = match self.operator {
                    Operator::Erode => u8::max_value(),
                    Operator::Dilate => u8::min_value(),
                };

                let mut output_pixel = Pixel {
                    r: initial,
                    g: initial,
                    b: initial,
                    a: initial,
                };

                for (_x, _y, pixel) in PixelRectangle::within(
                    &input_1.surface(),
                    bounds,
                    kernel_bounds,
                    EdgeMode::None,
                ) {
                    let op = match self.operator {
                        Operator::Erode => min,
                        Operator::Dilate => max,
                    };

                    output_pixel.r = op(output_pixel.r, pixel.r);
                    output_pixel.g = op(output_pixel.g, pixel.g);
                    output_pixel.b = op(output_pixel.b, pixel.b);
                    output_pixel.a = op(output_pixel.a, pixel.a);
                }

                data.set_pixel(stride, output_pixel, x, y);
            }
        });

        Ok(FilterOutput {
            surface: surface.share()?,
            bounds,
        })
    }
}

impl FilterEffect for FeMorphology {
    fn resolve(&self, _node: &Node) -> Result<(Primitive, PrimitiveParams), FilterError> {
        Ok((
            self.base.clone(),
            PrimitiveParams::Morphology(self.params.clone()),
        ))
    }
}

impl Parse for Operator {
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
        Ok(parse_identifiers!(
            parser,
            "erode" => Operator::Erode,
            "dilate" => Operator::Dilate,
        )?)
    }
}
<commit_msg>(#698): Put limit on feMorphology radii to prevent thread hang<commit_after>use std::cmp::{max, min};

use cssparser::Parser;
use markup5ever::{expanded_name, local_name, namespace_url, ns};

use crate::document::AcquiredNodes;
use crate::drawing_ctx::DrawingCtx;
use crate::element::{ElementResult, SetAttributes};
use crate::error::*;
use crate::node::Node;
use crate::parsers::{NonNegative, NumberOptionalNumber, Parse, ParseValue};
use crate::property_defs::ColorInterpolationFilters;
use crate::rect::IRect;
use crate::surface_utils::{
    iterators::{PixelRectangle, Pixels},
    shared_surface::ExclusiveImageSurface,
    EdgeMode, ImageSurfaceDataExt, Pixel,
};
use crate::xml::Attributes;

use super::bounds::BoundsBuilder;
use super::context::{FilterContext, FilterOutput};
use super::{FilterEffect, FilterError, Input, Primitive, PrimitiveParams};

/// Enumeration of the possible morphology operations.
#[derive(Clone)]
enum Operator {
    Erode,
    Dilate,
}

enum_default!(Operator, Operator::Erode);

/// The `feMorphology` filter primitive.
#[derive(Default)]
pub struct FeMorphology {
    base: Primitive,
    params: Morphology,
}

/// Resolved `feMorphology` primitive for rendering.
#[derive(Clone, Default)]
pub struct Morphology {
    in1: Input,
    operator: Operator,
    radius: (f64, f64),
}

impl SetAttributes for FeMorphology {
    fn set_attributes(&mut self, attrs: &Attributes) -> ElementResult {
        self.params.in1 = self.base.parse_one_input(attrs)?;

        for (attr, value) in attrs.iter() {
            match attr.expanded() {
                expanded_name!("", "operator") => self.params.operator = attr.parse(value)?,
                expanded_name!("", "radius") => {
                    let NumberOptionalNumber(NonNegative(x), NonNegative(y)) = attr.parse(value)?;
                    self.params.radius = (x, y);
                }
                _ => (),
            }
        }

        Ok(())
    }
}

impl Morphology {
    pub fn render(
        &self,
        bounds_builder: BoundsBuilder,
        ctx: &FilterContext,
        acquired_nodes: &mut AcquiredNodes<'_>,
        draw_ctx: &mut DrawingCtx,
    ) -> Result<FilterOutput, FilterError> {
        // Although https://www.w3.org/TR/filter-effects/#propdef-color-interpolation-filters does not mention
        // feMorphology as being one of the primitives that does *not* use that property,
        // the SVG1.1 test for filters-morph-01-f.svg fails if we pass the value from the ComputedValues here (that
        // document does not specify the color-interpolation-filters property, so it defaults to linearRGB).
        // So, we pass Auto, which will get resolved to SRGB, and that makes that test pass.
        //
        // I suppose erosion/dilation doesn't care about the color space of the source image?

        let input_1 = ctx.get_input(
            acquired_nodes,
            draw_ctx,
            &self.in1,
            ColorInterpolationFilters::Auto,
        )?;
        let bounds: IRect = bounds_builder
            .add_input(&input_1)
            .compute(ctx)
            .clipped
            .into();

        let (rx, ry) = self.radius;
        let (rx, ry) = ctx.paffine().transform_distance(rx, ry);

        // The radii can become negative here due to the transform.
        // Additionally The radii being excessively large causes cpu hangups
        let (rx, ry) = (rx.abs().min(10.0), ry.abs().min(10.0));

        let mut surface = ExclusiveImageSurface::new(
            ctx.source_graphic().width(),
            ctx.source_graphic().height(),
            input_1.surface().surface_type(),
        )?;

        surface.modify(&mut |data, stride| {
            for (x, y, _pixel) in Pixels::within(input_1.surface(), bounds) {
                // Compute the kernel rectangle bounds.
                let kernel_bounds = IRect::new(
                    (f64::from(x) - rx).floor() as i32,
                    (f64::from(y) - ry).floor() as i32,
                    (f64::from(x) + rx).ceil() as i32 + 1,
                    (f64::from(y) + ry).ceil() as i32 + 1,
                );

                // Compute the new pixel values.
                let initial = match self.operator {
                    Operator::Erode => u8::max_value(),
                    Operator::Dilate => u8::min_value(),
                };

                let mut output_pixel = Pixel {
                    r: initial,
                    g: initial,
                    b: initial,
                    a: initial,
                };

                for (_x, _y, pixel) in PixelRectangle::within(
                    &input_1.surface(),
                    bounds,
                    kernel_bounds,
                    EdgeMode::None,
                ) {
                    let op = match self.operator {
                        Operator::Erode => min,
                        Operator::Dilate => max,
                    };

                    output_pixel.r = op(output_pixel.r, pixel.r);
                    output_pixel.g = op(output_pixel.g, pixel.g);
                    output_pixel.b = op(output_pixel.b, pixel.b);
                    output_pixel.a = op(output_pixel.a, pixel.a);
                }

                data.set_pixel(stride, output_pixel, x, y);
            }
        });

        Ok(FilterOutput {
            surface: surface.share()?,
            bounds,
        })
    }
}

impl FilterEffect for FeMorphology {
    fn resolve(&self, _node: &Node) -> Result<(Primitive, PrimitiveParams), FilterError> {
        Ok((
            self.base.clone(),
            PrimitiveParams::Morphology(self.params.clone()),
        ))
    }
}

impl Parse for Operator {
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
        Ok(parse_identifiers!(
            parser,
            "erode" => Operator::Erode,
            "dilate" => Operator::Dilate,
        )?)
    }
}
<|endoftext|>


<commit_before>use ra_text_edit::AtomTextEdit;
use ra_syntax::{
    AstNode, SyntaxNode, SourceFile, TextUnit, TextRange,
    ast,
    algo::{find_leaf_at_offset, find_covering_node, find_node_at_offset},
    SyntaxKind::*,
};
use hir::source_binder;

use crate::{db, FilePosition};

/// `CompletionContext` is created early during completion to figure out, where
/// exactly is the cursor, syntax-wise.
#[derive(Debug)]
pub(crate) struct CompletionContext<'a> {
    pub(super) db: &'a db::RootDatabase,
    pub(super) offset: TextUnit,
    pub(super) leaf: &'a SyntaxNode,
    pub(super) module: Option<hir::Module>,
    pub(super) function: Option<hir::Function>,
    pub(super) function_syntax: Option<&'a ast::FnDef>,
    pub(super) use_item_syntax: Option<&'a ast::UseItem>,
    pub(super) is_param: bool,
    /// A single-indent path, like `foo`.
    pub(super) is_trivial_path: bool,
    /// If not a trivial, path, the prefix (qualifier).
    pub(super) path_prefix: Option<hir::Path>,
    pub(super) after_if: bool,
    /// `true` if we are a statement or a last expr in the block.
    pub(super) can_be_stmt: bool,
    /// Something is typed at the "top" level, in module or impl/trait.
    pub(super) is_new_item: bool,
    /// The receiver if this is a field or method access, i.e. writing something.<|>
    pub(super) dot_receiver: Option<&'a ast::Expr>,
    /// If this is a call (method or function) in particular, i.e. the () are already there.
    pub(super) is_call: bool,
}

impl<'a> CompletionContext<'a> {
    pub(super) fn new(
        db: &'a db::RootDatabase,
        original_file: &'a SourceFile,
        position: FilePosition,
    ) -> Option<CompletionContext<'a>> {
        let module = source_binder::module_from_position(db, position);
        let leaf = find_leaf_at_offset(original_file.syntax(), position.offset).left_biased()?;
        let mut ctx = CompletionContext {
            db,
            leaf,
            offset: position.offset,
            module,
            function: None,
            function_syntax: None,
            use_item_syntax: None,
            is_param: false,
            is_trivial_path: false,
            path_prefix: None,
            after_if: false,
            can_be_stmt: false,
            is_new_item: false,
            dot_receiver: None,
            is_call: false,
        };
        ctx.fill(original_file, position.offset);
        Some(ctx)
    }

    // The range of the identifier that is being completed.
    // This is purely advisory and can be used, for example, to highlight this range in the editor.
    // Clients are expected to ignore this field.
    pub(crate) fn source_range(&self) -> TextRange {
        self.leaf.range()
    }

    fn fill(&mut self, original_file: &'a SourceFile, offset: TextUnit) {
        // Insert a fake ident to get a valid parse tree. We will use this file
        // to determine context, though the original_file will be used for
        // actual completion.
        let file = {
            let edit = AtomTextEdit::insert(offset, "intellijRulezz".to_string());
            original_file.reparse(&edit)
        };

        // First, let's try to complete a reference to some declaration.
        if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(file.syntax(), offset) {
            // Special case, `trait T { fn foo(i_am_a_name_ref) {} }`.
            // See RFC#1685.
            if is_node::<ast::Param>(name_ref.syntax()) {
                self.is_param = true;
                return;
            }
            self.classify_name_ref(original_file, name_ref);
        }

        // Otherwise, see if this is a declaration. We can use heuristics to
        // suggest declaration names, see `CompletionKind::Magic`.
        if let Some(name) = find_node_at_offset::<ast::Name>(file.syntax(), offset) {
            if is_node::<ast::Param>(name.syntax()) {
                self.is_param = true;
                return;
            }
        }
    }
    fn classify_name_ref(&mut self, original_file: &'a SourceFile, name_ref: &ast::NameRef) {
        let name_range = name_ref.syntax().range();
        let top_node = name_ref
            .syntax()
            .ancestors()
            .take_while(|it| it.range() == name_range)
            .last()
            .unwrap();

        match top_node.parent().map(|it| it.kind()) {
            Some(SOURCE_FILE) | Some(ITEM_LIST) => {
                self.is_new_item = true;
                return;
            }
            _ => (),
        }

        self.use_item_syntax = self.leaf.ancestors().find_map(ast::UseItem::cast);

        self.function_syntax = self
            .leaf
            .ancestors()
            .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
            .find_map(ast::FnDef::cast);
        match (&self.module, self.function_syntax) {
            (Some(module), Some(fn_def)) => {
                let function = source_binder::function_from_module(self.db, module, fn_def);
                self.function = Some(function);
            }
            _ => (),
        }

        let parent = match name_ref.syntax().parent() {
            Some(it) => it,
            None => return,
        };
        if let Some(segment) = ast::PathSegment::cast(parent) {
            let path = segment.parent_path();
            if let Some(mut path) = hir::Path::from_ast(path) {
                if !path.is_ident() {
                    path.segments.pop().unwrap();
                    self.path_prefix = Some(path);
                    return;
                }
            }
            if path.qualifier().is_none() {
                self.is_trivial_path = true;

                // Find either enclosing expr statement (thing with `;`) or a
                // block. If block, check that we are the last expr.
                self.can_be_stmt = name_ref
                    .syntax()
                    .ancestors()
                    .find_map(|node| {
                        if let Some(stmt) = ast::ExprStmt::cast(node) {
                            return Some(stmt.syntax().range() == name_ref.syntax().range());
                        }
                        if let Some(block) = ast::Block::cast(node) {
                            return Some(
                                block.expr().map(|e| e.syntax().range())
                                    == Some(name_ref.syntax().range()),
                            );
                        }
                        None
                    })
                    .unwrap_or(false);

                if let Some(off) = name_ref.syntax().range().start().checked_sub(2.into()) {
                    if let Some(if_expr) =
                        find_node_at_offset::<ast::IfExpr>(original_file.syntax(), off)
                    {
                        if if_expr.syntax().range().end() < name_ref.syntax().range().start() {
                            self.after_if = true;
                        }
                    }
                }
            }
            self.is_call = path
                .syntax()
                .parent()
                .and_then(ast::PathExpr::cast)
                .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast))
                .is_some()
        }
        if let Some(field_expr) = ast::FieldExpr::cast(parent) {
            // The receiver comes before the point of insertion of the fake
            // ident, so it should have the same range in the non-modified file
            self.dot_receiver = field_expr
                .expr()
                .map(|e| e.syntax().range())
                .and_then(|r| find_node_with_range(original_file.syntax(), r));
        }
        if let Some(method_call_expr) = ast::MethodCallExpr::cast(parent) {
            // As above
            self.dot_receiver = method_call_expr
                .expr()
                .map(|e| e.syntax().range())
                .and_then(|r| find_node_with_range(original_file.syntax(), r));
            self.is_call = true;
        }
    }
}

fn find_node_with_range<N: AstNode>(syntax: &SyntaxNode, range: TextRange) -> Option<&N> {
    let node = find_covering_node(syntax, range);
    node.ancestors().find_map(N::cast)
}

fn is_node<N: AstNode>(node: &SyntaxNode) -> bool {
    match node.ancestors().filter_map(N::cast).next() {
        None => false,
        Some(n) => n.syntax().range() == node.range(),
    }
}
<commit_msg>workaround for trigger character<commit_after>use ra_text_edit::AtomTextEdit;
use ra_syntax::{
    AstNode, SyntaxNode, SourceFile, TextUnit, TextRange,
    ast,
    algo::{find_leaf_at_offset, find_covering_node, find_node_at_offset},
    SyntaxKind::*,
};
use hir::source_binder;

use crate::{db, FilePosition};

/// `CompletionContext` is created early during completion to figure out, where
/// exactly is the cursor, syntax-wise.
#[derive(Debug)]
pub(crate) struct CompletionContext<'a> {
    pub(super) db: &'a db::RootDatabase,
    pub(super) offset: TextUnit,
    pub(super) leaf: &'a SyntaxNode,
    pub(super) module: Option<hir::Module>,
    pub(super) function: Option<hir::Function>,
    pub(super) function_syntax: Option<&'a ast::FnDef>,
    pub(super) use_item_syntax: Option<&'a ast::UseItem>,
    pub(super) is_param: bool,
    /// A single-indent path, like `foo`.
    pub(super) is_trivial_path: bool,
    /// If not a trivial, path, the prefix (qualifier).
    pub(super) path_prefix: Option<hir::Path>,
    pub(super) after_if: bool,
    /// `true` if we are a statement or a last expr in the block.
    pub(super) can_be_stmt: bool,
    /// Something is typed at the "top" level, in module or impl/trait.
    pub(super) is_new_item: bool,
    /// The receiver if this is a field or method access, i.e. writing something.<|>
    pub(super) dot_receiver: Option<&'a ast::Expr>,
    /// If this is a call (method or function) in particular, i.e. the () are already there.
    pub(super) is_call: bool,
}

impl<'a> CompletionContext<'a> {
    pub(super) fn new(
        db: &'a db::RootDatabase,
        original_file: &'a SourceFile,
        position: FilePosition,
    ) -> Option<CompletionContext<'a>> {
        let module = source_binder::module_from_position(db, position);
        let leaf = find_leaf_at_offset(original_file.syntax(), position.offset).left_biased()?;
        let mut ctx = CompletionContext {
            db,
            leaf,
            offset: position.offset,
            module,
            function: None,
            function_syntax: None,
            use_item_syntax: None,
            is_param: false,
            is_trivial_path: false,
            path_prefix: None,
            after_if: false,
            can_be_stmt: false,
            is_new_item: false,
            dot_receiver: None,
            is_call: false,
        };
        ctx.fill(original_file, position.offset);
        Some(ctx)
    }

    // The range of the identifier that is being completed.
    // This is purely advisory and can be used, for example, to highlight this range in the editor.
    // Clients are expected to ignore this field.
    pub(crate) fn source_range(&self) -> TextRange {
        match self.leaf.kind() {
            // workaroud when completion is triggered by trigger characters.
            DOT | COLONCOLON => TextRange::from_to(self.offset, self.offset),
            _ => self.leaf.range()
        }
    }

    fn fill(&mut self, original_file: &'a SourceFile, offset: TextUnit) {
        // Insert a fake ident to get a valid parse tree. We will use this file
        // to determine context, though the original_file will be used for
        // actual completion.
        let file = {
            let edit = AtomTextEdit::insert(offset, "intellijRulezz".to_string());
            original_file.reparse(&edit)
        };

        // First, let's try to complete a reference to some declaration.
        if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(file.syntax(), offset) {
            // Special case, `trait T { fn foo(i_am_a_name_ref) {} }`.
            // See RFC#1685.
            if is_node::<ast::Param>(name_ref.syntax()) {
                self.is_param = true;
                return;
            }
            self.classify_name_ref(original_file, name_ref);
        }

        // Otherwise, see if this is a declaration. We can use heuristics to
        // suggest declaration names, see `CompletionKind::Magic`.
        if let Some(name) = find_node_at_offset::<ast::Name>(file.syntax(), offset) {
            if is_node::<ast::Param>(name.syntax()) {
                self.is_param = true;
                return;
            }
        }
    }
    fn classify_name_ref(&mut self, original_file: &'a SourceFile, name_ref: &ast::NameRef) {
        let name_range = name_ref.syntax().range();
        let top_node = name_ref
            .syntax()
            .ancestors()
            .take_while(|it| it.range() == name_range)
            .last()
            .unwrap();

        match top_node.parent().map(|it| it.kind()) {
            Some(SOURCE_FILE) | Some(ITEM_LIST) => {
                self.is_new_item = true;
                return;
            }
            _ => (),
        }

        self.use_item_syntax = self.leaf.ancestors().find_map(ast::UseItem::cast);

        self.function_syntax = self
            .leaf
            .ancestors()
            .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
            .find_map(ast::FnDef::cast);
        match (&self.module, self.function_syntax) {
            (Some(module), Some(fn_def)) => {
                let function = source_binder::function_from_module(self.db, module, fn_def);
                self.function = Some(function);
            }
            _ => (),
        }

        let parent = match name_ref.syntax().parent() {
            Some(it) => it,
            None => return,
        };
        if let Some(segment) = ast::PathSegment::cast(parent) {
            let path = segment.parent_path();
            if let Some(mut path) = hir::Path::from_ast(path) {
                if !path.is_ident() {
                    path.segments.pop().unwrap();
                    self.path_prefix = Some(path);
                    return;
                }
            }
            if path.qualifier().is_none() {
                self.is_trivial_path = true;

                // Find either enclosing expr statement (thing with `;`) or a
                // block. If block, check that we are the last expr.
                self.can_be_stmt = name_ref
                    .syntax()
                    .ancestors()
                    .find_map(|node| {
                        if let Some(stmt) = ast::ExprStmt::cast(node) {
                            return Some(stmt.syntax().range() == name_ref.syntax().range());
                        }
                        if let Some(block) = ast::Block::cast(node) {
                            return Some(
                                block.expr().map(|e| e.syntax().range())
                                    == Some(name_ref.syntax().range()),
                            );
                        }
                        None
                    })
                    .unwrap_or(false);

                if let Some(off) = name_ref.syntax().range().start().checked_sub(2.into()) {
                    if let Some(if_expr) =
                        find_node_at_offset::<ast::IfExpr>(original_file.syntax(), off)
                    {
                        if if_expr.syntax().range().end() < name_ref.syntax().range().start() {
                            self.after_if = true;
                        }
                    }
                }
            }
            self.is_call = path
                .syntax()
                .parent()
                .and_then(ast::PathExpr::cast)
                .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast))
                .is_some()
        }
        if let Some(field_expr) = ast::FieldExpr::cast(parent) {
            // The receiver comes before the point of insertion of the fake
            // ident, so it should have the same range in the non-modified file
            self.dot_receiver = field_expr
                .expr()
                .map(|e| e.syntax().range())
                .and_then(|r| find_node_with_range(original_file.syntax(), r));
        }
        if let Some(method_call_expr) = ast::MethodCallExpr::cast(parent) {
            // As above
            self.dot_receiver = method_call_expr
                .expr()
                .map(|e| e.syntax().range())
                .and_then(|r| find_node_with_range(original_file.syntax(), r));
            self.is_call = true;
        }
    }
}

fn find_node_with_range<N: AstNode>(syntax: &SyntaxNode, range: TextRange) -> Option<&N> {
    let node = find_covering_node(syntax, range);
    node.ancestors().find_map(N::cast)
}

fn is_node<N: AstNode>(node: &SyntaxNode) -> bool {
    match node.ancestors().filter_map(N::cast).next() {
        None => false,
        Some(n) => n.syntax().range() == node.range(),
    }
}
<|endoftext|>


<commit_before>use std::{
    collections::HashMap,
    env,
    ffi::OsString,
    fs,
    path::{Path, PathBuf},
};

pub use eyre;
use eyre::{bail, Result};
pub use fs_utils;
pub use hash_utils;
pub use libcnb;
use libcnb::{
    build::BuildContext,
    data::{
        build_plan::{Provide, Require},
        buildpack::BuildpackId,
    },
    libcnb_runtime_build, libcnb_runtime_detect, BuildArgs, DetectArgs, Platform,
};
use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none;
pub use tokio;
pub use toml;
pub use tracing;

/// The stack id
///
/// Currently a constants (cause there's only one :) but in the
/// future there may be more e.g. `stencila.stacks.jammy`
const CNB_STACK_ID: &str = "stencila.stacks.focal";

/// Test whether the current CNB platform is Stencila
pub fn platform_dir_is_stencila(platform_dir: &Path) -> bool {
    platform_dir.join("env").join("STENCILA_VERSION").exists()
}

/// Test whether the current CNB platform is Stencila
pub fn platform_is_stencila(platform: &impl Platform) -> bool {
    platform.env().contains_key("STENCILA_VERSION")
}

/// Is this a local build (ie. using Stencila, not Pack as platform)
pub fn is_local_build<B: libcnb::Buildpack>(context: &BuildContext<B>) -> bool {
    platform_is_stencila(&context.platform)
}

/// Is this a CNB build (e.g. using Pack as the platform)
pub fn is_cnb_build<B: libcnb::Buildpack>(context: &BuildContext<B>) -> bool {
    !is_local_build(context)
}

/// String used by buildpacks to indicate that a tool version
/// was determined from the (highest) version found on the system
pub const SYSTEM_INSTALLED: &str = "<system>";

/// A local trait for buildpacks that extends `libcnb::Buildpack`
///
/// Why? To provide some additional introspection and the ability
/// to compile several buildpacks into a single binary and call
/// their `detect` and `build` methods.
pub trait BuildpackTrait: libcnb::Buildpack {
    /// Get the content of the `buildpack.toml` file
    fn toml() -> &'static str;

    /// Get the buildpack's spec (a.k.a. descriptor) from the `buildpack.toml`
    fn spec() -> Result<BuildpackToml> {
        let toml = Self::toml();
        let spec = toml::from_str::<BuildpackToml>(toml)?;
        Ok(spec)
    }

    /// Get the buildpack's `id` from the `buildpack.toml`
    fn id(&self) -> Result<BuildpackId> {
        let spec = Self::spec()?;
        Ok(spec.buildpack.id)
    }

    /// Ensure the buildpack's runtime directory exists and return its path
    ///
    /// Both the `detect` and `build` methods require that `buildpack.toml`
    /// be available on disk.
    fn ensure_dir(&self) -> Result<String> {
        let id = self.id()?;
        let dir = buildpacks_dir()?.join(id.to_string());
        fs::create_dir_all(&dir)?;

        // Write the `buildpack.toml` to the directory
        let toml_path = dir.join("buildpack.toml");
        fs::write(&toml_path, Self::toml())?;

        Ok(dir.display().to_string())
    }

    /// Test whether any of the files exists in the working directory
    ///
    /// A convenience method for use in `detect`.
    fn any_exist(paths: &[&str]) -> bool {
        for path in paths {
            if PathBuf::from(path).exists() {
                return true;
            }
        }
        false
    }

    /// Test whether a file contains a string
    ///
    /// A convenience method for use in `detect`.
    fn file_contains(file: &str, string: &str) -> bool {
        fs::read_to_string(file)
            .map(|content| content.contains(string))
            .unwrap_or(false)
    }

    /// Prepend a path to the `PATH` env var
    fn prepend_path(first: &Path) -> Result<OsString> {
        if let Some(path) = env::var_os("PATH") {
            let mut paths = vec![first.into()];
            paths.append(&mut env::split_paths(&path).collect::<Vec<_>>());
            Ok(env::join_paths(paths)?)
        } else {
            Ok(first.into())
        }
    }

    /// Parses a `.tool-versions` file (if any)
    ///
    /// A convenience method for use in `detect`. If there is no such file, then
    /// the returned map will be empty.
    ///
    /// Note that each line of `.tool-versions` can have multiple versions, but
    /// that this takes the first.
    fn tool_versions() -> HashMap<String, String> {
        match fs::read_to_string(".tool-versions") {
            Ok(content) => content
                .lines()
                .filter_map(|line| {
                    let mut parts = line.split_whitespace();
                    let name = match parts.next() {
                        Some(name) => name.to_string(),
                        None => return None,
                    };
                    let version = parts.next().unwrap_or("*").to_string();
                    Some((name, version))
                })
                .collect(),
            Err(..) => HashMap::new(),
        }
    }

    /// Generate `Require` (with metadata) and `Provide` objects
    ///
    /// A convenience method for use in `build` method.
    fn require_and_provide(
        name: impl AsRef<str>,
        source: impl AsRef<str>,
        desc: impl AsRef<str>,
    ) -> (Require, Provide) {
        let mut require = Require::new(name.as_ref());
        if !source.as_ref().is_empty() {
            require.metadata.insert(
                "source".to_string(),
                toml::Value::String(source.as_ref().to_string()),
            );
        }
        if !desc.as_ref().is_empty() {
            require.metadata.insert(
                "description".to_string(),
                toml::Value::String(desc.as_ref().to_string()),
            );
        }

        let provide = Provide::new(name.as_ref());

        (require, provide)
    }

    /// Split the `name` of a buildpack plan into two parts
    ///
    /// A convenience method for used in `build`.
    fn split_entry_name(name: &str) -> (String, Vec<String>) {
        let mut parts = name.split(' ');
        (
            parts.next().unwrap_or_default().to_string(),
            parts.map(String::from).collect::<Vec<String>>(),
        )
    }

    /// Run the buildpack's `detect` method
    fn detect_with(&self, platform_dir: &Path, build_plan: &Path) -> Result<i32>
    where
        Self: Sized,
    {
        env::set_var("CNB_STACK_ID", CNB_STACK_ID);

        let buildpack_dir = self.ensure_dir()?;
        env::set_var("CNB_BUILDPACK_DIR", buildpack_dir);

        match libcnb_runtime_detect(
            self,
            DetectArgs {
                platform_dir_path: PathBuf::from(platform_dir),
                build_plan_path: PathBuf::from(build_plan),
            },
        ) {
            Ok(code) => Ok(code),
            Err(error) => bail!(
                "While running `detect` for buildpack `{}`: {}",
                self.id()?,
                error
            ),
        }
    }

    /// Run the buildpack's `build` method
    fn build_with(
        &self,
        layers_dir: &Path,
        platform_dir: &Path,
        buildpack_plan: &Path,
    ) -> Result<i32>
    where
        Self: Sized,
    {
        env::set_var("CNB_STACK_ID", CNB_STACK_ID);

        let buildpack_dir = self.ensure_dir()?;
        env::set_var("CNB_BUILDPACK_DIR", buildpack_dir);

        match libcnb_runtime_build(
            self,
            BuildArgs {
                layers_dir_path: PathBuf::from(layers_dir),
                platform_dir_path: PathBuf::from(platform_dir),
                buildpack_plan_path: PathBuf::from(buildpack_plan),
            },
        ) {
            Ok(code) => Ok(code),
            Err(error) => bail!(
                "While running `build` for buildpack `{}`: {}",
                self.id()?,
                error
            ),
        }
    }
}

/// Get the path of the directory where buildpacks stored on the users machine
pub fn buildpacks_dir() -> Result<PathBuf> {
    let user_data_dir = dirs::data_dir()
        .unwrap_or_else(|| env::current_dir().expect("Should always be able to get current dir"));
    let dir = match env::consts::OS {
        "macos" | "windows" => user_data_dir.join("Stencila").join("Buildpacks"),
        _ => user_data_dir.join("stencila").join("buildpacks"),
    };
    Ok(dir)
}

/// Generate a locally unique tag for an image based on a directory path
///
/// Build a container image with a tag `<name>-<hash>` where `<name>` is the
/// name of the directory containing the `Dockerfile` and `<hash>` is the 12-character
/// truncated SHA256 hash of its path (to avoid clashes between directories with the same name).
pub fn tag_for_path(path: &Path) -> String {
    let name = path
        .file_name()
        .map(|name| name.to_string_lossy().to_string())
        .unwrap_or_else(|| "unnamed".to_string());

    let mut hash = hash_utils::str_sha256_hex(&path.display().to_string());
    hash.truncate(12);

    [&name, "-", &hash].concat()
}

// The `libcnb` crate provides similar structs to those below, often with stronger typing,
// but those do not implement `Serialize` or `Clone` and so for our purposes
// it was easier to reimplement them here. This decision should be revisited at some time.

/// A Buildpack Descriptor (`buildpack.toml`)
///
/// Used primarily to read in and display the spec for a buildpack for
/// use in commands such a `stencila buildpacks show <label>`.
///
/// See https://github.com/buildpacks/spec/blob/main/buildpack.md#buildpacktoml-toml
#[skip_serializing_none]
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct BuildpackToml {
    pub api: String,
    pub buildpack: Buildpack,
    pub stacks: Option<Vec<BuildpackStack>>,
    pub order: Option<Vec<BuildpackGroup>>,
    pub metadata: Option<serde_json::Value>,
}

#[skip_serializing_none]
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Buildpack {
    pub id: BuildpackId,
    pub version: String,
    pub name: String,
    pub clear_env: Option<bool>,
    pub homepage: Option<String>,
    pub description: Option<String>,
    pub keywords: Option<Vec<String>>,
    #[serde(rename = "sbom-formats")]
    pub sbom_formats: Option<Vec<String>>,
    pub licenses: Option<Vec<BuildpackLicense>>,
}

#[skip_serializing_none]
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct BuildpackLicense {
    pub r#type: Option<String>,
    pub uri: Option<String>,
}

#[skip_serializing_none]
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct BuildpackStack {
    pub id: String,
    pub mixins: Option<Vec<String>>,
}

#[derive(Debug, Default, Clone, Deserialize, Serialize)]
#[serde(default)]
pub struct BuildpackGroup {
    pub id: String,
    pub version: String,
    pub optional: bool,
}

/// A Build Plan
///
/// Generated by the `detect` method of a buildpack (if it matches against a folder).
///
/// See https://github.com/buildpacks/spec/blob/main/buildpack.md#build-plan-toml
#[skip_serializing_none]
#[derive(Debug, Default, Deserialize, Serialize)]
pub struct BuildPlan {
    pub provides: Option<Vec<BuildPlanProvides>>,
    pub requires: Option<Vec<BuildPlanRequires>>,
    pub or: Option<Vec<BuildPlanOr>>,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct BuildPlanProvides {
    pub name: String,
}

#[skip_serializing_none]
#[derive(Debug, Deserialize, Serialize)]
pub struct BuildPlanRequires {
    pub name: String,
    pub metadata: Option<toml::Value>,
}

#[skip_serializing_none]
#[derive(Debug, Default, Deserialize, Serialize)]
pub struct BuildPlanOr {
    pub provides: Option<BuildPlanProvides>,
    pub requires: Option<BuildPlanRequires>,
}

/// A Buildpack Plan
///
/// Passed to the `build` method of each buildpack involved in the build.
///
/// See https://github.com/buildpacks/spec/blob/main/buildpack.md#buildpack-plan-toml
#[derive(Debug, Default, Deserialize, Serialize)]
#[serde(default)]
pub struct BuildpackPlan {
    pub entries: Vec<BuildPlanRequires>,
}

/// Runtime entry point function
///
/// Just sets up logging and calls `libcnb::libcnb_runtime`
/// to do the actual work.
pub fn runtime<B: libcnb::Buildpack>(buildpack: &B) {
    tracing_subscriber::fmt().init();
    libcnb::libcnb_runtime(buildpack);
}

/// Generate a `main` function for a buildpack
#[macro_export]
macro_rules! buildpack_main {
    ($buildpack:ident) => {
        #[tokio::main]
        async fn main() {
            buildpack::runtime(&$buildpack);
        }
    };
}
<commit_msg>feat(Buildpack): Add common layer metadata types<commit_after>use std::{
    collections::HashMap,
    env,
    ffi::OsString,
    fs,
    path::{Path, PathBuf},
};

pub use eyre;
use eyre::{bail, Result};
pub use fs_utils;
pub use hash_utils;
pub use libcnb;
use libcnb::{
    build::BuildContext,
    data::{
        build_plan::{Provide, Require},
        buildpack::BuildpackId,
    },
    libcnb_runtime_build, libcnb_runtime_detect, BuildArgs, DetectArgs, Platform,
};
use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none;
pub use tokio;
pub use toml;
pub use tracing;

/// The stack id
///
/// Currently a constant (cause there's only one :) but in the
/// future there may be more e.g. `stencila.stacks.jammy`
const CNB_STACK_ID: &str = "stencila.stacks.focal";

/// Test whether the current CNB platform is Stencila
pub fn platform_dir_is_stencila(platform_dir: &Path) -> bool {
    platform_dir.join("env").join("STENCILA_VERSION").exists()
}

/// Test whether the current CNB platform is Stencila
pub fn platform_is_stencila(platform: &impl Platform) -> bool {
    platform.env().contains_key("STENCILA_VERSION")
}

/// Is this a local build (ie. using Stencila, not Pack as platform)
pub fn is_local_build<B: libcnb::Buildpack>(context: &BuildContext<B>) -> bool {
    platform_is_stencila(&context.platform)
}

/// Is this a CNB build (e.g. using Pack as the platform)
pub fn is_cnb_build<B: libcnb::Buildpack>(context: &BuildContext<B>) -> bool {
    !is_local_build(context)
}

/// String used by buildpacks to indicate that a tool version
/// was determined from the (highest) version found on the system
pub const SYSTEM_INSTALLED: &str = "<system>";

/// A local trait for buildpacks that extends `libcnb::Buildpack`
///
/// Why? To provide some additional introspection and the ability
/// to compile several buildpacks into a single binary and call
/// their `detect` and `build` methods.
pub trait BuildpackTrait: libcnb::Buildpack {
    /// Get the content of the `buildpack.toml` file
    fn toml() -> &'static str;

    /// Get the buildpack's spec (a.k.a. descriptor) from the `buildpack.toml`
    fn spec() -> Result<BuildpackToml> {
        let toml = Self::toml();
        let spec = toml::from_str::<BuildpackToml>(toml)?;
        Ok(spec)
    }

    /// Get the buildpack's `id` from the `buildpack.toml`
    fn id(&self) -> Result<BuildpackId> {
        let spec = Self::spec()?;
        Ok(spec.buildpack.id)
    }

    /// Ensure the buildpack's runtime directory exists and return its path
    ///
    /// Both the `detect` and `build` methods require that `buildpack.toml`
    /// be available on disk.
    fn ensure_dir(&self) -> Result<String> {
        let id = self.id()?;
        let dir = buildpacks_dir()?.join(id.to_string());
        fs::create_dir_all(&dir)?;

        // Write the `buildpack.toml` to the directory
        let toml_path = dir.join("buildpack.toml");
        fs::write(&toml_path, Self::toml())?;

        Ok(dir.display().to_string())
    }

    /// Test whether any of the files exists in the working directory
    ///
    /// A convenience method for use in `detect`.
    fn any_exist(paths: &[&str]) -> bool {
        for path in paths {
            if PathBuf::from(path).exists() {
                return true;
            }
        }
        false
    }

    /// Test whether a file contains a string
    ///
    /// A convenience method for use in `detect`.
    fn file_contains(file: &str, string: &str) -> bool {
        fs::read_to_string(file)
            .map(|content| content.contains(string))
            .unwrap_or(false)
    }

    /// Prepend a path to the `PATH` env var
    fn prepend_path(first: &Path) -> Result<OsString> {
        if let Some(path) = env::var_os("PATH") {
            let mut paths = vec![first.into()];
            paths.append(&mut env::split_paths(&path).collect::<Vec<_>>());
            Ok(env::join_paths(paths)?)
        } else {
            Ok(first.into())
        }
    }

    /// Parses a `.tool-versions` file (if any)
    ///
    /// A convenience method for use in `detect`. If there is no such file, then
    /// the returned map will be empty.
    ///
    /// Note that each line of `.tool-versions` can have multiple versions, but
    /// that this takes the first.
    fn tool_versions() -> HashMap<String, String> {
        match fs::read_to_string(".tool-versions") {
            Ok(content) => content
                .lines()
                .filter_map(|line| {
                    let mut parts = line.split_whitespace();
                    let name = match parts.next() {
                        Some(name) => name.to_string(),
                        None => return None,
                    };
                    let version = parts.next().unwrap_or("*").to_string();
                    Some((name, version))
                })
                .collect(),
            Err(..) => HashMap::new(),
        }
    }

    /// Generate `Require` (with metadata) and `Provide` objects
    ///
    /// A convenience method for use in `build` method.
    fn require_and_provide(
        name: impl AsRef<str>,
        source: impl AsRef<str>,
        desc: impl AsRef<str>,
    ) -> (Require, Provide) {
        let mut require = Require::new(name.as_ref());
        if !source.as_ref().is_empty() {
            require.metadata.insert(
                "source".to_string(),
                toml::Value::String(source.as_ref().to_string()),
            );
        }
        if !desc.as_ref().is_empty() {
            require.metadata.insert(
                "description".to_string(),
                toml::Value::String(desc.as_ref().to_string()),
            );
        }

        let provide = Provide::new(name.as_ref());

        (require, provide)
    }

    /// Split the `name` of a buildpack plan into two parts
    ///
    /// A convenience method for used in `build`.
    fn split_entry_name(name: &str) -> (String, Vec<String>) {
        let mut parts = name.split(' ');
        (
            parts.next().unwrap_or_default().to_string(),
            parts.map(String::from).collect::<Vec<String>>(),
        )
    }

    /// Run the buildpack's `detect` method
    fn detect_with(&self, platform_dir: &Path, build_plan: &Path) -> Result<i32>
    where
        Self: Sized,
    {
        env::set_var("CNB_STACK_ID", CNB_STACK_ID);

        let buildpack_dir = self.ensure_dir()?;
        env::set_var("CNB_BUILDPACK_DIR", buildpack_dir);

        match libcnb_runtime_detect(
            self,
            DetectArgs {
                platform_dir_path: PathBuf::from(platform_dir),
                build_plan_path: PathBuf::from(build_plan),
            },
        ) {
            Ok(code) => Ok(code),
            Err(error) => bail!(
                "While running `detect` for buildpack `{}`: {}",
                self.id()?,
                error
            ),
        }
    }

    /// Run the buildpack's `build` method
    fn build_with(
        &self,
        layers_dir: &Path,
        platform_dir: &Path,
        buildpack_plan: &Path,
    ) -> Result<i32>
    where
        Self: Sized,
    {
        env::set_var("CNB_STACK_ID", CNB_STACK_ID);

        let buildpack_dir = self.ensure_dir()?;
        env::set_var("CNB_BUILDPACK_DIR", buildpack_dir);

        match libcnb_runtime_build(
            self,
            BuildArgs {
                layers_dir_path: PathBuf::from(layers_dir),
                platform_dir_path: PathBuf::from(platform_dir),
                buildpack_plan_path: PathBuf::from(buildpack_plan),
            },
        ) {
            Ok(code) => Ok(code),
            Err(error) => bail!(
                "While running `build` for buildpack `{}`: {}",
                self.id()?,
                error
            ),
        }
    }
}

/// Metadata for a layer that uses a hash to determine existing layer strategy
#[derive(Clone, Deserialize, Serialize)]
pub struct LayerHashMetadata {
    pub hash: String,
}

/// Metadata for a layer that uses a version to determine existing layer strategy
#[derive(Clone, Deserialize, Serialize)]
pub struct LayerVersionMetadata {
    pub version: String,
}

/// Get the path of the directory where buildpacks stored on the users machine
pub fn buildpacks_dir() -> Result<PathBuf> {
    let user_data_dir = dirs::data_dir()
        .unwrap_or_else(|| env::current_dir().expect("Should always be able to get current dir"));
    let dir = match env::consts::OS {
        "macos" | "windows" => user_data_dir.join("Stencila").join("Buildpacks"),
        _ => user_data_dir.join("stencila").join("buildpacks"),
    };
    Ok(dir)
}

/// Generate a locally unique tag for an image based on a directory path
///
/// Build a container image with a tag `<name>-<hash>` where `<name>` is the
/// name of the directory containing the `Dockerfile` and `<hash>` is the 12-character
/// truncated SHA256 hash of its path (to avoid clashes between directories with the same name).
pub fn tag_for_path(path: &Path) -> String {
    let name = path
        .file_name()
        .map(|name| name.to_string_lossy().to_string())
        .unwrap_or_else(|| "unnamed".to_string());

    let mut hash = hash_utils::str_sha256_hex(&path.display().to_string());
    hash.truncate(12);

    [&name, "-", &hash].concat()
}

// The `libcnb` crate provides similar structs to those below, often with stronger typing,
// but those do not implement `Serialize` or `Clone` and so for our purposes
// it was easier to reimplement them here. This decision should be revisited at some time.

/// A Buildpack Descriptor (`buildpack.toml`)
///
/// Used primarily to read in and display the spec for a buildpack for
/// use in commands such a `stencila buildpacks show <label>`.
///
/// See https://github.com/buildpacks/spec/blob/main/buildpack.md#buildpacktoml-toml
#[skip_serializing_none]
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct BuildpackToml {
    pub api: String,
    pub buildpack: Buildpack,
    pub stacks: Option<Vec<BuildpackStack>>,
    pub order: Option<Vec<BuildpackGroup>>,
    pub metadata: Option<serde_json::Value>,
}

#[skip_serializing_none]
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Buildpack {
    pub id: BuildpackId,
    pub version: String,
    pub name: String,
    pub clear_env: Option<bool>,
    pub homepage: Option<String>,
    pub description: Option<String>,
    pub keywords: Option<Vec<String>>,
    #[serde(rename = "sbom-formats")]
    pub sbom_formats: Option<Vec<String>>,
    pub licenses: Option<Vec<BuildpackLicense>>,
}

#[skip_serializing_none]
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct BuildpackLicense {
    pub r#type: Option<String>,
    pub uri: Option<String>,
}

#[skip_serializing_none]
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct BuildpackStack {
    pub id: String,
    pub mixins: Option<Vec<String>>,
}

#[derive(Debug, Default, Clone, Deserialize, Serialize)]
#[serde(default)]
pub struct BuildpackGroup {
    pub id: String,
    pub version: String,
    pub optional: bool,
}

/// A Build Plan
///
/// Generated by the `detect` method of a buildpack (if it matches against a folder).
///
/// See https://github.com/buildpacks/spec/blob/main/buildpack.md#build-plan-toml
#[skip_serializing_none]
#[derive(Debug, Default, Deserialize, Serialize)]
pub struct BuildPlan {
    pub provides: Option<Vec<BuildPlanProvides>>,
    pub requires: Option<Vec<BuildPlanRequires>>,
    pub or: Option<Vec<BuildPlanOr>>,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct BuildPlanProvides {
    pub name: String,
}

#[skip_serializing_none]
#[derive(Debug, Deserialize, Serialize)]
pub struct BuildPlanRequires {
    pub name: String,
    pub metadata: Option<toml::Value>,
}

#[skip_serializing_none]
#[derive(Debug, Default, Deserialize, Serialize)]
pub struct BuildPlanOr {
    pub provides: Option<BuildPlanProvides>,
    pub requires: Option<BuildPlanRequires>,
}

/// A Buildpack Plan
///
/// Passed to the `build` method of each buildpack involved in the build.
///
/// See https://github.com/buildpacks/spec/blob/main/buildpack.md#buildpack-plan-toml
#[derive(Debug, Default, Deserialize, Serialize)]
#[serde(default)]
pub struct BuildpackPlan {
    pub entries: Vec<BuildPlanRequires>,
}

/// Runtime entry point function
///
/// Just sets up logging and calls `libcnb::libcnb_runtime`
/// to do the actual work.
pub fn runtime<B: libcnb::Buildpack>(buildpack: &B) {
    tracing_subscriber::fmt().init();
    libcnb::libcnb_runtime(buildpack);
}

/// Generate a `main` function for a buildpack
#[macro_export]
macro_rules! buildpack_main {
    ($buildpack:ident) => {
        #[tokio::main]
        async fn main() {
            buildpack::runtime(&$buildpack);
        }
    };
}
<|endoftext|>


<commit_before>use tikv::proto::raftpb::*;
use protobuf::{self, ProtobufEnum};
use tikv::raft::*;
use tikv::raft::storage::MemStorage;
use std::sync::Arc;
use test_raft::{new_storage, new_test_config, new_message, new_entry, empty_entry, new_snapshot};

fn new_peer(id: u64) -> Peer {
    Peer { id: id, ..Default::default() }
}

fn entry(t: EntryType, term: u64, i: u64, data: Option<Vec<u8>>) -> Entry {
    let mut e = Entry::new();
    e.set_index(i);
    e.set_term(term);
    if let Some(d) = data {
        e.set_data(d);
    }
    e.set_entry_type(t);
    e
}

fn conf_change(t: ConfChangeType, node_id: u64) -> ConfChange {
    let mut cc = ConfChange::new();
    cc.set_change_type(t);
    cc.set_node_id(node_id);
    cc
}

fn soft_state(lead: u64, state: StateRole) -> SoftState {
    SoftState {
        lead: lead,
        raft_state: state,
    }
}

fn hard_state(t: u64, c: u64, v: u64) -> HardState {
    let mut hs = HardState::new();
    hs.set_term(t);
    hs.set_commit(c);
    hs.set_vote(v);
    hs
}

fn new_ready(ss: Option<SoftState>,
             hs: Option<HardState>,
             entries: Vec<Entry>,
             committed_entries: Vec<Entry>)
             -> Ready {
    Ready {
        ss: ss,
        hs: hs,
        entries: entries,
        committed_entries: committed_entries,
        ..Default::default()
    }
}

fn new_raw_node(id: u64,
                peers: Vec<u64>,
                election: usize,
                heartbeat: usize,
                storage: Arc<MemStorage>,
                peer_nodes: Vec<Peer>)
                -> RawNode<MemStorage> {
    RawNode::new(&new_test_config(id, peers, election, heartbeat, storage),
                 &peer_nodes)
        .unwrap()
}

// test_raw_node_step ensures that RawNode.Step ignore local message.
#[test]
fn test_raw_node_step() {
    for msg_t in MessageType::values() {
        let mut raw_node = new_raw_node(1, vec![], 10, 1, new_storage(), vec![new_peer(1)]);
        let res = raw_node.step(new_message(0, 0, *msg_t, 0));
        // local msg should be ignored.
        if vec![MessageType::MsgBeat,
                MessageType::MsgHup,
                MessageType::MsgUnreachable,
                MessageType::MsgSnapStatus]
               .contains(msg_t) {
            assert_eq!(res, Err(Error::StepLocalMsg));
        }
    }
}

// test_raw_node_propose_and_conf_change ensures that RawNode.propose and RawNode.propose_conf_change
// send the given proposal and ConfChange to the underlying raft.
#[test]
fn test_raw_node_propose_and_conf_change() {
    let s = new_storage();
    let mut raw_node = new_raw_node(1, vec![], 10, 1, s.clone(), vec![new_peer(1)]);
    raw_node.campaign().expect("");
    let mut proposed = false;
    let mut last_index;
    let mut ccdata = vec![];
    loop {
        let rd = raw_node.ready();
        s.wl().append(&rd.entries).expect("");
        // Once we are the leader, propose a command and a ConfChange.
        if !proposed && rd.ss.is_some() && rd.ss.as_ref().unwrap().lead == raw_node.raft.id {
            raw_node.propose("somedata".as_bytes().to_vec()).expect("");

            let cc = conf_change(ConfChangeType::ConfChangeAddNode, 1);
            ccdata = protobuf::Message::write_to_bytes(&cc).unwrap();
            raw_node.propose_conf_change(cc).expect("");

            proposed = true;
        }
        raw_node.advance(rd);

        // Exit when we have four entries: one ConfChange, one no-op for the election,
        // our proposed command and proposed ConfChange.
        last_index = s.last_index().unwrap();
        if last_index >= 4 {
            break;
        }
    }

    let entries = s.entries(last_index - 1, last_index + 1, NO_LIMIT).unwrap();
    assert_eq!(entries.len(), 2);
    assert_eq!(entries[0].get_data(), "somedata".as_bytes());
    assert_eq!(entries[1].get_entry_type(), EntryType::EntryConfChange);
    assert_eq!(entries[1].get_data(), &*ccdata);
}

// test_raw_node_start ensures that a node can be started correctly. The node should
// start with correct configuration change entries, and can accept and commit
// proposals.
#[test]
fn test_raw_node_start() {
    let cc = conf_change(ConfChangeType::ConfChangeAddNode, 1);
    let ccdata = protobuf::Message::write_to_bytes(&cc).unwrap();
    let wents = vec![new_ready(Some(soft_state(1, StateRole::Leader)),
                               Some(hard_state(2, 2, 1)),
                               vec![
                entry(EntryType::EntryConfChange, 1, 1, Some(ccdata.clone())),
                empty_entry(2, 2),
            ],
                               vec![
                entry(EntryType::EntryConfChange, 1, 1, Some(ccdata.clone())),
                empty_entry(2, 2),
            ]),
                     new_ready(None,
                               Some(hard_state(2, 3, 1)),
                               vec![new_entry(2, 3, Some("foo"))],
                               vec![new_entry(2, 3, Some("foo"))])];

    let store = new_storage();
    let mut raw_node = new_raw_node(1, vec![], 10, 1, store.clone(), vec![new_peer(1)]);
    raw_node.campaign().expect("");
    let rd = raw_node.ready();
    info!("rd {:?}", &rd);
    assert_eq!(rd, wents[0]);
    store.wl().append(&rd.entries).expect("");
    raw_node.advance(rd);

    raw_node.propose("foo".as_bytes().to_vec()).expect("");
    let rd = raw_node.ready();
    assert_eq!(rd, wents[1]);
    store.wl().append(&rd.entries).expect("");
    raw_node.advance(rd);
    assert!(!raw_node.has_ready());
}

#[test]
fn test_raw_node_restart() {
    let entries = vec![
        empty_entry(1, 1),
        new_entry(1, 2, Some("foo")),
    ];
    let st = hard_state(1, 1, 0);

    let want = new_ready(None, None, vec![], entries[..1].to_vec());

    let store = new_storage();
    store.wl().set_hardstate(st);
    store.wl().append(&entries).expect("");
    let mut raw_node = new_raw_node(1, vec![], 10, 1, store, vec![]);
    let rd = raw_node.ready();
    assert_eq!(rd, want);
    raw_node.advance(rd);
    assert!(!raw_node.has_ready());
}

#[test]
fn test_raw_node_restart_from_snapshot() {
    let snap = new_snapshot(2, 1, vec![1, 2]);
    let entries = vec![new_entry(1, 3, Some("foo"))];
    let st = hard_state(1, 3, 0);

    let want = new_ready(None, None, vec![], entries.clone());

    let s = new_storage();
    s.wl().set_hardstate(st);
    s.wl().apply_snapshot(snap).expect("");
    s.wl().append(&entries).expect("");
    let mut raw_node = new_raw_node(1, vec![], 10, 1, s, vec![]);
    let rd = raw_node.ready();
    assert_eq!(rd, want);
    raw_node.advance(rd);
    assert!(!raw_node.has_ready());
}
<commit_msg>address comment<commit_after>use tikv::proto::raftpb::*;
use protobuf::{self, ProtobufEnum};
use tikv::raft::*;
use tikv::raft::storage::MemStorage;
use std::sync::Arc;
use test_raft::{new_storage, new_test_config, new_message, new_entry, empty_entry, new_snapshot};

fn new_peer(id: u64) -> Peer {
    Peer { id: id, ..Default::default() }
}

fn entry(t: EntryType, term: u64, i: u64, data: Option<Vec<u8>>) -> Entry {
    let mut e = Entry::new();
    e.set_index(i);
    e.set_term(term);
    if let Some(d) = data {
        e.set_data(d);
    }
    e.set_entry_type(t);
    e
}

fn conf_change(t: ConfChangeType, node_id: u64) -> ConfChange {
    let mut cc = ConfChange::new();
    cc.set_change_type(t);
    cc.set_node_id(node_id);
    cc
}

fn soft_state(lead: u64, state: StateRole) -> SoftState {
    SoftState {
        lead: lead,
        raft_state: state,
    }
}

fn hard_state(t: u64, c: u64, v: u64) -> HardState {
    let mut hs = HardState::new();
    hs.set_term(t);
    hs.set_commit(c);
    hs.set_vote(v);
    hs
}

fn new_ready(ss: Option<SoftState>,
             hs: Option<HardState>,
             entries: Vec<Entry>,
             committed_entries: Vec<Entry>)
             -> Ready {
    Ready {
        ss: ss,
        hs: hs,
        entries: entries,
        committed_entries: committed_entries,
        ..Default::default()
    }
}

fn new_raw_node(id: u64,
                peers: Vec<u64>,
                election: usize,
                heartbeat: usize,
                storage: Arc<MemStorage>,
                peer_nodes: Vec<Peer>)
                -> RawNode<MemStorage> {
    RawNode::new(&new_test_config(id, peers, election, heartbeat, storage),
                 &peer_nodes)
        .unwrap()
}

// test_raw_node_step ensures that RawNode.Step ignore local message.
#[test]
fn test_raw_node_step() {
    for msg_t in MessageType::values() {
        let mut raw_node = new_raw_node(1, vec![], 10, 1, new_storage(), vec![new_peer(1)]);
        let res = raw_node.step(new_message(0, 0, *msg_t, 0));
        // local msg should be ignored.
        if vec![MessageType::MsgBeat,
                MessageType::MsgHup,
                MessageType::MsgUnreachable,
                MessageType::MsgSnapStatus]
               .contains(msg_t) {
            assert_eq!(res, Err(Error::StepLocalMsg));
        }
    }
}

// test_raw_node_propose_and_conf_change ensures that RawNode.propose and RawNode.propose_conf_change
// send the given proposal and ConfChange to the underlying raft.
#[test]
fn test_raw_node_propose_and_conf_change() {
    let s = new_storage();
    let mut raw_node = new_raw_node(1, vec![], 10, 1, s.clone(), vec![new_peer(1)]);
    raw_node.campaign().expect("");
    let mut proposed = false;
    let mut last_index;
    let mut ccdata = vec![];
    loop {
        let rd = raw_node.ready();
        s.wl().append(&rd.entries).expect("");
        // Once we are the leader, propose a command and a ConfChange.
        if !proposed && rd.ss.is_some() && rd.ss.as_ref().unwrap().lead == raw_node.raft.id {
            raw_node.propose("somedata".as_bytes().to_vec()).expect("");

            let cc = conf_change(ConfChangeType::ConfChangeAddNode, 1);
            ccdata = protobuf::Message::write_to_bytes(&cc).unwrap();
            raw_node.propose_conf_change(cc).expect("");

            proposed = true;
        }
        raw_node.advance(rd);

        // Exit when we have four entries: one ConfChange, one no-op for the election,
        // our proposed command and proposed ConfChange.
        last_index = s.last_index().unwrap();
        if last_index >= 4 {
            break;
        }
    }

    let entries = s.entries(last_index - 1, last_index + 1, NO_LIMIT).unwrap();
    assert_eq!(entries.len(), 2);
    assert_eq!(entries[0].get_data(), "somedata".as_bytes());
    assert_eq!(entries[1].get_entry_type(), EntryType::EntryConfChange);
    assert_eq!(entries[1].get_data(), &*ccdata);
}

// test_raw_node_start ensures that a node can be started correctly. The node should
// start with correct configuration change entries, and can accept and commit
// proposals.
#[test]
fn test_raw_node_start() {
    let cc = conf_change(ConfChangeType::ConfChangeAddNode, 1);
    let ccdata = protobuf::Message::write_to_bytes(&cc).unwrap();
    let wants = vec![new_ready(Some(soft_state(1, StateRole::Leader)),
                               Some(hard_state(2, 2, 1)),
                               vec![
                entry(EntryType::EntryConfChange, 1, 1, Some(ccdata.clone())),
                empty_entry(2, 2),
            ],
                               vec![
                entry(EntryType::EntryConfChange, 1, 1, Some(ccdata.clone())),
                empty_entry(2, 2),
            ]),
                     new_ready(None,
                               Some(hard_state(2, 3, 1)),
                               vec![new_entry(2, 3, Some("foo"))],
                               vec![new_entry(2, 3, Some("foo"))])];

    let store = new_storage();
    let mut raw_node = new_raw_node(1, vec![], 10, 1, store.clone(), vec![new_peer(1)]);
    raw_node.campaign().expect("");
    let rd = raw_node.ready();
    info!("rd {:?}", &rd);
    assert_eq!(rd, wants[0]);
    store.wl().append(&rd.entries).expect("");
    raw_node.advance(rd);

    raw_node.propose("foo".as_bytes().to_vec()).expect("");
    let rd = raw_node.ready();
    assert_eq!(rd, wants[1]);
    store.wl().append(&rd.entries).expect("");
    raw_node.advance(rd);
    assert!(!raw_node.has_ready());
}

#[test]
fn test_raw_node_restart() {
    let entries = vec![
        empty_entry(1, 1),
        new_entry(1, 2, Some("foo")),
    ];
    let st = hard_state(1, 1, 0);

    let want = new_ready(None, None, vec![], entries[..1].to_vec());

    let store = new_storage();
    store.wl().set_hardstate(st);
    store.wl().append(&entries).expect("");
    let mut raw_node = new_raw_node(1, vec![], 10, 1, store, vec![]);
    let rd = raw_node.ready();
    assert_eq!(rd, want);
    raw_node.advance(rd);
    assert!(!raw_node.has_ready());
}

#[test]
fn test_raw_node_restart_from_snapshot() {
    let snap = new_snapshot(2, 1, vec![1, 2]);
    let entries = vec![new_entry(1, 3, Some("foo"))];
    let st = hard_state(1, 3, 0);

    let want = new_ready(None, None, vec![], entries.clone());

    let s = new_storage();
    s.wl().set_hardstate(st);
    s.wl().apply_snapshot(snap).expect("");
    s.wl().append(&entries).expect("");
    let mut raw_node = new_raw_node(1, vec![], 10, 1, s, vec![]);
    let rd = raw_node.ready();
    assert_eq!(rd, want);
    raw_node.advance(rd);
    assert!(!raw_node.has_ready());
}
<|endoftext|>