<commit_before>use utils::bigint::M256;
use utils::gas::Gas;
use utils::address::Address;

use super::{Memory, VectorMemory, Stack, VectorStack, PC, VectorPC, Result, Error};
use blockchain::{Block, FakeVectorBlock};
use transaction::{Transaction, VectorTransaction};

use std::borrow::BorrowMut;
use std::marker::PhantomData;

pub trait Machine {
    type P: PC;
    type M: Memory;
    type Sta: Stack;
    type T: Transaction;
    type B: Block;
    type Sub: Machine;

    fn pc(&self) -> &Self::P;
    fn pc_mut(&mut self) -> &mut Self::P;
    fn memory(&self) -> &Self::M;
    fn memory_mut(&mut self) -> &mut Self::M;
    fn stack(&self) -> &Self::Sta;
    fn stack_mut(&mut self) -> &mut Self::Sta;

    fn transaction(&self) -> &Self::T;
    fn block(&self) -> &Self::B;
    fn block_mut(&mut self) -> &mut Self::B;

    fn use_gas(&mut self, gas: Gas);
    fn used_gas(&self) -> Gas;
    fn return_values(&self) -> &[u8];
    fn set_return_values(&mut self, data: &[u8]);

    fn fork<F: FnOnce(&mut Self::Sub)>(&mut self, gas: Gas, from:
                                       Address, to: Address, value: M256,
                                       memory_in_start: M256,
                                       memory_in_len: M256,
                                       memory_out_start: M256,
                                       memory_out_len: M256, f: F);

    fn step(&mut self) -> Result<()> where Self: Sized {
        if self.pc().stopped() {
            return Err(Error::Stopped)
        }

        if !self.available_gas().is_valid() {
            return Err(Error::EmptyGas)
        }

        let opcode = self.pc_mut().read_opcode();
        let before = opcode.gas_cost_before(self)?;
        self.use_gas(before);
        opcode.run(self)?;
        let after = opcode.gas_cost_after(self);
        self.use_gas(after);

        Ok(())
    }

    fn fire(&mut self) -> Result<()> where Self: Sized {
        loop {
            let result = self.step();

            if result.is_err() {
                match result.err().unwrap() {
                    Error::Stopped => return Ok(()),
                    err => return Err(err),
                }
            }
        }
    }

    fn available_gas(&self) -> Gas {
        self.transaction().gas_limit() - self.used_gas()
    }
}

pub struct VectorMachine<B0, BR> {
    pc: VectorPC,
    memory: VectorMemory,
    stack: VectorStack,
    transaction: VectorTransaction,
    return_values: Vec<u8>,
    block: Option
,
    used_gas: Gas,
    _block_marker: PhantomData<B0>,
}

impl<B0: Block, BR: AsRef<B0> + AsMut<B0>> VectorMachine<B0, BR> {
    pub fn new(code: &[u8], data: &[u8], gas_limit: Gas,
               transaction: VectorTransaction, block: BR) -> Self {
        VectorMachine {
            pc: VectorPC::new(code),
            memory: VectorMemory::new(),
            stack: VectorStack::new(),
            transaction: transaction,
            return_values: Vec::new(),
            block: Some(block),
            used_gas: Gas::zero(),
            _block_marker: PhantomData,
        }
    }
}

impl<B0: Block, BR: AsRef<B0> + AsMut<B0>> Machine for VectorMachine<B0, BR> {
    type P = VectorPC;
    type M = VectorMemory;
    type Sta = VectorStack;
    type T = VectorTransaction;
    type B = B0;
    type Sub = Self;

    fn return_values(&self) -> &[u8] {
        self.return_values.as_ref()
    }

    fn set_return_values(&mut self, val: &[u8]) {
        self.return_values = val.into();
    }

    fn use_gas(&mut self, gas: Gas) {
        self.used_gas += gas;
    }

    fn used_gas(&self) -> Gas {
        self.used_gas
    }

    fn pc(&self) -> &Self::P {
        &self.pc
    }

    fn pc_mut(&mut self) -> &mut Self::P {
        &mut self.pc
    }

    fn memory(&self) -> &Self::M {
        &self.memory
    }

    fn memory_mut(&mut self) -> &mut Self::M {
        &mut self.memory
    }

    fn stack(&self) -> &Self::Sta {
        &self.stack
    }

    fn stack_mut(&mut self) -> &mut Self::Sta {
        &mut self.stack
    }

    fn transaction(&self) -> &Self::T {
        &self.transaction
    }

    fn block(&self) -> &Self::B {
        self.block.as_ref().unwrap().as_ref()
    }

    fn block_mut(&mut self) -> &mut Self::B {
        self.block.as_mut().unwrap().as_mut()
    }

    fn fork<F: FnOnce(&mut Self::Sub)>(&mut self, gas: Gas, from: Address, to: Address,
                                       value: M256,
                                       memory_in_start: M256, memory_in_len: M256,
                                       memory_out_start: M256, memory_out_len: M256,
                                       f: F) {
        use std::mem::swap;

        let from = from;
        let mem_in_start: usize = memory_in_start.into();
        let mem_in_len: usize = memory_in_len.into();
        let mem_in_end: usize = mem_in_start + mem_in_len;
        let mem_in: Vec<u8> = self.memory().as_ref()[mem_in_start..mem_in_end].into();

        let mut submachine = Self {
            pc: VectorPC::new(if to == from { self.pc().code() }
                              else { self.block().account_code(to) }),
            memory: VectorMemory::new(),
            stack: VectorStack::new(),
            transaction: VectorTransaction::message_call(from, to, value, mem_in.as_ref(), gas),
            return_values: Vec::new(),
            block: None,
            used_gas: Gas::zero(),
            _block_marker: PhantomData,
        };

        // We swap the block into the sub-machine if necessary. The
        // current old block should never be referenced and will be
        // replaced back when the call finishes.
        swap(&mut self.block, &mut submachine.block);
        f(self);
        swap(&mut self.block, &mut submachine.block);

        let mem_out_start: usize = memory_out_start.into();
        let mem_out_len: usize = memory_out_len.into();
        let mem_out_end: usize = mem_out_start + mem_out_len;

        for i in 0..mem_out_end {
            self.memory_mut().write_raw(memory_out_start + i.into(), submachine.return_values[i]);
        }
    }
}

pub type FakeVectorMachine = VectorMachine<FakeVectorBlock, Box<FakeVectorBlock>>;

impl FakeVectorMachine {
    pub fn fake(code: &[u8], data: &[u8], gas_limit: Gas) -> FakeVectorMachine {
        VectorMachine::new(code, data, gas_limit,
                           VectorTransaction::message_call(Address::default(), Address::default(),
                                                           M256::zero(), data, gas_limit),
                           Box::new(FakeVectorBlock::new()))
    }
}
<commit_msg>Problem: gas may be deducted when the opcode step fails Solution: deduct the gas usage after the opcode runs<commit_after>use utils::bigint::M256;
use utils::gas::Gas;
use utils::address::Address;

use super::{Memory, VectorMemory, Stack, VectorStack, PC, VectorPC, Result, Error};
use blockchain::{Block, FakeVectorBlock};
use transaction::{Transaction, VectorTransaction};

use std::borrow::BorrowMut;
use std::marker::PhantomData;

pub trait Machine {
    type P: PC;
    type M: Memory;
    type Sta: Stack;
    type T: Transaction;
    type B: Block;
    type Sub: Machine;

    fn pc(&self) -> &Self::P;
    fn pc_mut(&mut self) -> &mut Self::P;
    fn memory(&self) -> &Self::M;
    fn memory_mut(&mut self) -> &mut Self::M;
    fn stack(&self) -> &Self::Sta;
    fn stack_mut(&mut self) -> &mut Self::Sta;

    fn transaction(&self) -> &Self::T;
    fn block(&self) -> &Self::B;
    fn block_mut(&mut self) -> &mut Self::B;

    fn use_gas(&mut self, gas: Gas);
    fn used_gas(&self) -> Gas;
    fn return_values(&self) -> &[u8];
    fn set_return_values(&mut self, data: &[u8]);

    fn fork<F: FnOnce(&mut Self::Sub)>(&mut self, gas: Gas, from:
                                       Address, to: Address, value: M256,
                                       memory_in_start: M256,
                                       memory_in_len: M256,
                                       memory_out_start: M256,
                                       memory_out_len: M256, f: F);

    fn step(&mut self) -> Result<()> where Self: Sized {
        if self.pc().stopped() {
            return Err(Error::Stopped)
        }

        if !self.available_gas().is_valid() {
            return Err(Error::EmptyGas)
        }

        let opcode = self.pc_mut().read_opcode();
        let before = opcode.gas_cost_before(self)?;
        opcode.run(self)?;
        let after = opcode.gas_cost_after(self);

        self.use_gas(before);
        self.use_gas(after);

        Ok(())
    }

    fn fire(&mut self) -> Result<()> where Self: Sized {
        loop {
            let result = self.step();

            if result.is_err() {
                match result.err().unwrap() {
                    Error::Stopped => return Ok(()),
                    err => return Err(err),
                }
            }
        }
    }

    fn available_gas(&self) -> Gas {
        self.transaction().gas_limit() - self.used_gas()
    }
}

pub struct VectorMachine<B0, BR> {
    pc: VectorPC,
    memory: VectorMemory,
    stack: VectorStack,
    transaction: VectorTransaction,
    return_values: Vec<u8>,
    block: Option
,
    used_gas: Gas,
    _block_marker: PhantomData<B0>,
}

impl<B0: Block, BR: AsRef<B0> + AsMut<B0>> VectorMachine<B0, BR> {
    pub fn new(code: &[u8], data: &[u8], gas_limit: Gas,
               transaction: VectorTransaction, block: BR) -> Self {
        VectorMachine {
            pc: VectorPC::new(code),
            memory: VectorMemory::new(),
            stack: VectorStack::new(),
            transaction: transaction,
            return_values: Vec::new(),
            block: Some(block),
            used_gas: Gas::zero(),
            _block_marker: PhantomData,
        }
    }
}

impl<B0: Block, BR: AsRef<B0> + AsMut<B0>> Machine for VectorMachine<B0, BR> {
    type P = VectorPC;
    type M = VectorMemory;
    type Sta = VectorStack;
    type T = VectorTransaction;
    type B = B0;
    type Sub = Self;

    fn return_values(&self) -> &[u8] {
        self.return_values.as_ref()
    }

    fn set_return_values(&mut self, val: &[u8]) {
        self.return_values = val.into();
    }

    fn use_gas(&mut self, gas: Gas) {
        self.used_gas += gas;
    }

    fn used_gas(&self) -> Gas {
        self.used_gas
    }

    fn pc(&self) -> &Self::P {
        &self.pc
    }

    fn pc_mut(&mut self) -> &mut Self::P {
        &mut self.pc
    }

    fn memory(&self) -> &Self::M {
        &self.memory
    }

    fn memory_mut(&mut self) -> &mut Self::M {
        &mut self.memory
    }

    fn stack(&self) -> &Self::Sta {
        &self.stack
    }

    fn stack_mut(&mut self) -> &mut Self::Sta {
        &mut self.stack
    }

    fn transaction(&self) -> &Self::T {
        &self.transaction
    }

    fn block(&self) -> &Self::B {
        self.block.as_ref().unwrap().as_ref()
    }

    fn block_mut(&mut self) -> &mut Self::B {
        self.block.as_mut().unwrap().as_mut()
    }

    fn fork<F: FnOnce(&mut Self::Sub)>(&mut self, gas: Gas, from: Address, to: Address,
                                       value: M256,
                                       memory_in_start: M256, memory_in_len: M256,
                                       memory_out_start: M256, memory_out_len: M256,
                                       f: F) {
        use std::mem::swap;

        let from = from;
        let mem_in_start: usize = memory_in_start.into();
        let mem_in_len: usize = memory_in_len.into();
        let mem_in_end: usize = mem_in_start + mem_in_len;
        let mem_in: Vec<u8> = self.memory().as_ref()[mem_in_start..mem_in_end].into();

        let mut submachine = Self {
            pc: VectorPC::new(if to == from { self.pc().code() }
                              else { self.block().account_code(to) }),
            memory: VectorMemory::new(),
            stack: VectorStack::new(),
            transaction: VectorTransaction::message_call(from, to, value, mem_in.as_ref(), gas),
            return_values: Vec::new(),
            block: None,
            used_gas: Gas::zero(),
            _block_marker: PhantomData,
        };

        // We swap the block into the sub-machine if necessary. The
        // current old block should never be referenced and will be
        // replaced back when the call finishes.
        swap(&mut self.block, &mut submachine.block);
        f(self);
        swap(&mut self.block, &mut submachine.block);

        let mem_out_start: usize = memory_out_start.into();
        let mem_out_len: usize = memory_out_len.into();
        let mem_out_end: usize = mem_out_start + mem_out_len;

        for i in 0..mem_out_end {
            self.memory_mut().write_raw(memory_out_start + i.into(), submachine.return_values[i]);
        }
    }
}

pub type FakeVectorMachine = VectorMachine<FakeVectorBlock, Box<FakeVectorBlock>>;

impl FakeVectorMachine {
    pub fn fake(code: &[u8], data: &[u8], gas_limit: Gas) -> FakeVectorMachine {
        VectorMachine::new(code, data, gas_limit,
                           VectorTransaction::message_call(Address::default(), Address::default(),
                                                           M256::zero(), data, gas_limit),
                           Box::new(FakeVectorBlock::new()))
    }
}
<|endoftext|>


<commit_before>use std::io;
use std::io::stdio;
use std::collections;
use std::rand::{Rng, task_rng};

struct Difficulty {
    min: uint,
    max: uint
}

struct Hangman {
    state: int,
    word: String,
    letters: collections::HashSet<char>,
    matched: collections::HashSet<char>
}

fn main() {
    let hangman = Hangman::new();
}

impl Hangman {
    fn new() -> Hangman {
        println!("WELCOME TO HANGMAN");
        println!("Type 'bye' at any time to quit.");
        println!("Please enter a difficulty: easy, medium, or hard.");

        let difficulty = Hangman::get_difficulty();

        println!("Minimum word length set to {}", difficulty.min);
        println!("Maximum word length set to {}", difficulty.max);

        let words = Hangman::get_words(&difficulty);

        // pick a word

        let mut rng = task_rng();
        let rand_word: &String = rng.choose(&*words.as_slice()).expect("No word.");
        let mut letters = collections::HashSet::new();
        println!("{}", rand_word);
        for letter in rand_word.as_slice().chars() {
            letters.insert(letter);
        }

        Hangman {
            state: 0,
            word: *rand_word,
            letters: letters,
            matched: collections::HashSet::new()
        }

    }

    fn get_difficulty() -> Difficulty {
        let mut difficulty: Difficulty;
        loop {
            io::print(">");
            let input = &stdio::stdin()
                .read_line()
                .ok()
                .expect("Failed to read line.");

            let difficulty_opt = match input.as_slice() {
                "easy\n"   => Some(Difficulty { min: 3, max: 5 }),
                "medium\n" => Some(Difficulty { min: 5, max: 7 }),
                "hard\n"   => Some(Difficulty { min: 7, max: 9 }),
                _          => None
            };

            match input.as_slice() {
                "easy\n"   => difficulty = Difficulty { min: 3, max: 5 },
                "medium\n" => difficulty = Difficulty { min: 5, max: 7 },
                "hard\n"   => difficulty = Difficulty { min: 7, max: 9 },
                _          => continue
            }

//            difficulty = match difficulty_opt {
//                Some(d) => d,
//                None    => {
//                    println!("Invalid input.");
//                    println!("Please enter a difficulty: easy, medium, or hard:");
//                    continue
//                }
//            };
        }
        difficulty
    }

    fn get_words(difficulty: &Difficulty) -> Box<Vec<String>> {
        let mut words = Vec::new();

        let path = Path::new("/usr/share/dict/words");
        let words_file = io::File::open(&path).ok().expect("Error opening file.");
        let mut reader = io::BufferedReader::new(words_file);
        let mut counter = 0u;
        loop {
            match reader.read_line() {
                Ok(line) => {
                    if difficulty.min < line.len() && line.len() < difficulty.max {
                        words.push(line
                                   .as_slice()
                                   .trim_right_chars('\n')
                                   .to_string());
                        counter += 1;
                    }
                }
                Err(why) => match why.kind {
                    io::EndOfFile => break,
                    _             => panic!("Error reading file: {}", why)
                }
            }
        }
        println!("Read in {} words.", counter);
        box words
    }
}

<commit_msg>Well, hangman works... :)<commit_after>use std::io;
use std::io::stdio;
use std::collections;
use std::rand::{Rng, task_rng};

struct Difficulty {
    min: uint,
    max: uint
}

struct Hangman {
    state: int,
    word: String,
    letters: collections::HashSet<char>,
    matched: collections::HashSet<char>
}

fn main() {
    let mut hangman = Hangman::new();
    hangman.start()
}

impl Hangman {
    fn new() -> Hangman {
        println!("WELCOME TO HANGMAN");
        println!("Type 'bye' at any time to quit.");
        println!("Please enter a difficulty: easy, medium, or hard.");

        let difficulty = Hangman::get_difficulty();

        println!("Minimum word length set to {}", difficulty.min);
        println!("Maximum word length set to {}", difficulty.max);

        let words: Box<Vec<String>> = Hangman::get_words(&difficulty);

        // pick a word

        let mut rng = task_rng();
        let rand_word = rng.choose(words.as_slice()).expect("No word found.").clone();
        let mut letters: collections::HashSet<char> = collections::HashSet::new();
        println!("{}", rand_word);
        for letter in rand_word.as_slice().chars() {
            letters.insert(letter.clone());
        }

        Hangman {
            state: 0,
            word: rand_word,
            letters: letters.clone(),
            matched: collections::HashSet::new()
        }
    }

    fn get_difficulty() -> Difficulty {
        let mut difficulty: Option<Difficulty> = None;
        while difficulty.is_none() {
            print!(">");
            let input = &stdio::stdin()
                .read_line()
                .ok()
                .expect("Failed to read line.");

            match input.as_slice().trim_right_chars('\n') {
                "easy"   => difficulty = Some(Difficulty { min: 3, max: 5 }),
                "medium" => difficulty = Some(Difficulty { min: 5, max: 7 }),
                "hard"   => difficulty = Some(Difficulty { min: 7, max: 9 }),
                _          => continue
            }
        }
        difficulty.expect("No difficulty set.")
    }

    fn get_words(difficulty: &Difficulty) -> Box<Vec<String>> {
        let mut words = Vec::new();

        let path = Path::new("/usr/share/dict/words");
        let words_file = io::File::open(&path).ok().expect("Error opening file.");
        let mut reader = io::BufferedReader::new(words_file);
        let mut counter = 0u;
        loop {
            match reader.read_line() {
                Ok(line) => {
                    if difficulty.min < line.len() && line.len() < difficulty.max {
                        words.push(line
                                   .as_slice()
                                   .trim_right_chars('\n')
                                   .to_string());
                        counter += 1;
                    }
                }
                Err(why) => match why.kind {
                    io::EndOfFile => break,
                    _             => panic!("Error reading file: {}", why)
                }
            }
        }
        println!("Read in {} words.", counter);
        box words
    }

    fn start(&mut self) {
        let mut reader = stdio::stdin();

        self.print_state();
        println!("");
        print!(">");
        for line in reader.lock().lines() {
            if !self.is_over() {
                let input = line.ok().expect("Unable to read input."); // change this

                if input.as_slice().trim_right_chars('\n') == "bye" {
                    break
                } else {
                    match &input.as_slice().char_at(0) {
                        x if self.letters.contains(x) => {
                            self.matched.insert(x.clone());
                            self.print_state();
                            println!("");
                            print!(">");
                        },
                        _                             => {
                            self.state += 1;
                            self.print_state();
                            println!("");
                            print!(">");
                        }
                    }
                }
            } else {
                break
            }
        }

    }

    fn is_over(&self) -> bool {
        // do we have all the letters?
        if self.state == 6 {
            true
        } else if self.letters == self.matched {
            true
        } else {
            false
        }
    }

    fn print_state(&self) {
        println!("State: {}", self.state);

        for letter in self.word.as_slice().chars() {
            if self.matched.contains(&letter) {
                print!("{} ", letter)
            } else {
                print!("_ ")
            }
        }
    }
}
<|endoftext|>


<commit_before>
use canvas::CanvasId;
use elmesque::Element;
use graphics::character::CharacterCache;
use position::{Depth, Dimensions, Point, Positionable, Sizeable};
use std::any::Any;
use std::fmt::Debug;
use theme::Theme;
use ui::{self, GlyphCache, Ui, UiId, UserInput};

pub mod button;
pub mod drop_down_list;
pub mod envelope_editor;
pub mod label;
pub mod matrix;
pub mod number_dialer;
pub mod slider;
pub mod text_box;
pub mod toggle;
pub mod xy_pad;

/// Unique widget identifier. Each widget must use a unique `WidgetId` so that it's state can be
/// cached within the `Ui` type. The reason we use a usize is because widgets are cached within
/// a `Vec`, which is limited to a size of `usize` elements.
pub type WidgetId = usize;

/// A trait to be implemented by all Widget types.
pub trait Widget: Positionable + Sizeable + Sized {
    /// State to be stored within the `Ui`s widget cache. Take advantage of this type for any large
    /// allocations that you would like to avoid repeating between updates, or any calculations
    /// that you'd like to avoid repeating between calls to `update` and `draw`. Conrod will never
    /// clone the state, it will only ever be moved.
    type State: Any + PartialEq + ::std::fmt::Debug;
    /// Styling used by the widget to construct an Element. Styling is useful to have in its own
    /// abstraction in order to making Theme serializing easier. Conrod doesn't yet support
    /// serializing non-internal widget styling with the `Theme` type, but we hope to soon.
    type Style: Any + PartialEq + ::std::fmt::Debug;

    /// After building the widget, you call this method to set its current state into the given
    /// `Ui`. More precisely, the following will occur when calling this method:
    /// - The widget's previous state and style will be retrieved.
    /// - The widget's current `Style` will be retrieved (from the `Widget::style` method).
    /// - The widget's state will be updated (using the `Widget::udpate` method).
    /// - If the widget's state or style has changed, `Widget::draw` will be called to create the
    /// new Element for rendering.
    /// - The new State, Style and Element (if there is one) will be cached within the `Ui`.
    fn set<C>(self, id: WidgetId, ui: &mut Ui<C>) where C: CharacterCache {
        let kind = self.unique_kind();
        let new_style = self.style();
        let depth = self.get_depth();
        let pos = self.get_position();
        let (h_align, v_align) = self.get_alignment(&ui.theme);
        let dim = {
            let Ui { ref theme, ref glyph_cache, .. } = *ui;
            self.get_dimensions(theme, glyph_cache)
        };
        let xy = ui.get_xy(pos, dim, h_align, v_align);

        // Collect the previous state and style or initialise both if none exist.
        let maybe_prev_state = ui::get_widget_state::<C, Self>(ui, id, kind).map(|prev|{
            let PrevState { state, style, xy, dim, depth } = prev;
            (Some(style), State { state: state, xy: xy, dim: dim, depth: depth, })
        });
        let (maybe_prev_style, prev_state) = maybe_prev_state.unwrap_or_else(|| {
            (None, State { state: self.init_state(), dim: [0.0, 0.0], xy: [0.0, 0.0], depth: 0.0 })
        });

        // Determine the id of the canvas that the widget is attached to. If not given explicitly,
        // check the positioning to retrieve the Id from there.
        let maybe_canvas_id = self.canvas_id().or_else(|| ui::canvas_from_position(ui, pos));

        // Update the widget's state.
        let maybe_new_state = {
            // Construct a UserInput for the widget.
            let user_input = ui::user_input(ui, UiId::Widget(id), maybe_canvas_id);
            let Ui { ref theme, ref glyph_cache, .. } = *ui;
            self.update(&prev_state, xy, dim, user_input, &new_style, theme, glyph_cache)
        };

        // Check for whether or not the user input needs to be captured or uncaptured.
        {
            let new_state = match maybe_new_state {
                Some(ref new_state) => new_state,
                None => &prev_state.state
            };
            if Self::capture_mouse(&prev_state.state, new_state) {
                ui::mouse_captured_by(ui, UiId::Widget(id));
            }
            if Self::uncapture_mouse(&prev_state.state, new_state) {
                ui::mouse_uncaptured_by(ui, UiId::Widget(id));
            }
            if Self::capture_keyboard(&prev_state.state, new_state) {
                ui::keyboard_captured_by(ui, UiId::Widget(id));
            }
            if Self::uncapture_keyboard(&prev_state.state, new_state) {
                ui::keyboard_uncaptured_by(ui, UiId::Widget(id));
            }
        }

        // Determine whether or not the `State` has changed.
        let (state_has_changed, new_state) = {
            match maybe_new_state {
                Some(new_state) =>
                    (true, State { dim: dim, xy: xy, depth: depth, state: new_state }),
                None => {
                    let has_changed = xy != prev_state.xy
                        || dim != prev_state.dim
                        || depth != prev_state.depth;
                    (has_changed, State { dim: dim, xy: xy, depth: depth, ..prev_state })
                },
            }
        };

        // Determine whether or not the widget's `Style` has changed.
        let style_has_changed = match maybe_prev_style {
            Some(prev_style) => prev_style != new_style,
            None => false,
        };

        // Construct the widget's element.
        let maybe_new_element = if style_has_changed || state_has_changed {
            let Ui { ref theme, ref glyph_cache, .. } = *ui;
            Some(Self::draw(&new_state, &new_style, theme, glyph_cache))
        } else {
            None
        };

        // Store the new `State` and `Style` within the cache.
        let State { state, dim, xy, depth } = new_state;
        let store: Store<Self::State, Self::Style> = Store { state: state, style: new_style };
        ui::update_widget(ui, id, maybe_canvas_id, kind, store, dim, xy, depth, maybe_new_element);
    }

    /// Optionally override with the case that the widget should capture the mouse.
    fn capture_mouse(_prev: &Self::State, _new: &Self::State) -> bool { false }

    /// Optionally override with the case that the widget should capture the mouse.
    fn uncapture_mouse(_prev: &Self::State, _new: &Self::State) -> bool { false }

    /// Optionally override with the case that the widget should capture the mouse.
    fn capture_keyboard(_prev: &Self::State, _new: &Self::State) -> bool { false }

    /// Optionally override with the case that the widget should capture the mouse.
    fn uncapture_keyboard(_prev: &Self::State, _new: &Self::State) -> bool { false }

    /// Return the kind of the widget as a &'static str. Note that this must be unique from all
    /// other widgets' "unique kinds". This is used by conrod to help avoid UiId errors.
    fn unique_kind(&self) -> &'static str;

    /// Return the initial `State` of the Widget. The `Ui` will only call this once.
    fn init_state(&self) -> Self::State;

    /// Return the styling of the widget. The `Ui` will call this once prior to each `update`. It
    /// does this so that it can check for differences in `Style` in case a new `Element` needs to
    /// be constructed.
    fn style(&self) -> Self::Style;

    /// Return the canvas to which the Widget will be attached, if there is one.
    fn canvas_id(&self) -> Option<CanvasId> { None }

    /// Your widget's previous state is given to you as a parameter and it is your job to
    /// construct and return an Update that will be used to update the widget's cached state.
    fn update<'a, C>(self,
                     prev: &State<Self::State>,
                     xy: Point,
                     dim: Dimensions,
                     input: UserInput<'a>,
                     current_style: &Self::Style,
                     theme: &Theme,
                     glyph_cache: &GlyphCache<C>) -> Option<Self::State>
        where C: CharacterCache;

    /// Construct a renderable Element from the current styling and new state. This will *only* be
    /// called on the occasion that the widget's `Style` or `State` has changed. Keep this in mind
    /// when designing your widget's `Style` and `State` types.
    fn draw<C>(new_state: &State<Self::State>,
               current_style: &Self::Style,
               theme: &Theme,
               glyph_cache: &GlyphCache<C>) -> Element
        where C: CharacterCache;

}

/// Represents the unique cached state of a widget.
#[derive(PartialEq)]
pub struct State<T> {
    /// The state of the Widget.
    pub state: T,
    /// The rectangular dimensions of the Widget.
    pub dim: Dimensions,
    /// The position of the Widget given as [x, y] coordinates.
    pub xy: Point,
    /// The rendering depth for the Widget (the default is 0.0).
    pub depth: Depth,
}

/// The previous widget state to be returned by the Ui prior to a widget updating it's new state.
pub struct PrevState<W> where W: Widget {
    /// State that is unique to the widget.
    pub state: W::State,
    /// Unique styling state for the widget.
    pub style: W::Style,
    /// Previous dimensions of the Widget.
    pub dim: Dimensions,
    /// Previous position of the Widget.
    pub xy: Point,
    /// Previous rendering depth of the Widget.
    pub depth: Depth,
}

/// The state type that we'll dynamically cast to and from Any for storage within the Cache.
#[derive(Debug)]
pub struct Store<Sta, Sty>
    where
        Sta: Any  + Debug,
        Sty: Any  + Debug,
{
    pub state: Sta,
    pub style: Sty,
}

/// A widget element for storage within the Ui's `widget_cache`.
#[derive(Debug)]
pub struct Cached {
    pub maybe_state: Option<Box<Any>>,
    pub kind: &'static str,
    pub dim: Dimensions,
    pub xy: Point,
    pub depth: Depth,
    pub element: Element,
    pub has_updated: bool,
    pub maybe_canvas_id: Option<CanvasId>,
}

impl Cached {

    /// Construct an empty Widget for a vacant widget position within the Ui.
    pub fn empty() -> Cached {
        Cached {
            maybe_state: None,
            kind: "EMPTY",
            dim: [0.0, 0.0],
            xy: [0.0, 0.0],
            depth: 0.0,
            element: ::elmesque::element::empty(),
            has_updated: false,
            maybe_canvas_id: None,
        }
    }

}

<commit_msg>Improved Widget trait docs<commit_after>
use canvas::CanvasId;
use elmesque::Element;
use graphics::character::CharacterCache;
use position::{Depth, Dimensions, Point, Positionable, Sizeable};
use std::any::Any;
use std::fmt::Debug;
use theme::Theme;
use ui::{self, GlyphCache, Ui, UiId, UserInput};

pub mod button;
pub mod drop_down_list;
pub mod envelope_editor;
pub mod label;
pub mod matrix;
pub mod number_dialer;
pub mod slider;
pub mod text_box;
pub mod toggle;
pub mod xy_pad;

/// Unique widget identifier. Each widget must use a unique `WidgetId` so that it's state can be
/// cached within the `Ui` type. The reason we use a usize is because widgets are cached within
/// a `Vec`, which is limited to a size of `usize` elements.
pub type WidgetId = usize;

/// A trait to be implemented by all Widget types.
///
/// Methods that *must* be overridden:
/// - unique_kind
/// - init_state
/// - style
/// - update
/// - draw
///
/// Methods that can be optionally overridden:
/// - canvas_id
/// - capture_mouse
/// - uncapture_mouse
/// - capture_keyboard
/// - uncapture_keyboard
///
/// Methods that should not be overridden:
/// - set
///
pub trait Widget: Positionable + Sizeable + Sized {
    /// State to be stored within the `Ui`s widget cache. Take advantage of this type for any large
    /// allocations that you would like to avoid repeating between updates, or any calculations
    /// that you'd like to avoid repeating between calls to `update` and `draw`. Conrod will never
    /// clone the state, it will only ever be moved.
    type State: Any + PartialEq + ::std::fmt::Debug;
    /// Styling used by the widget to construct an Element. Styling is useful to have in its own
    /// abstraction in order to making Theme serializing easier. Conrod doesn't yet support
    /// serializing non-internal widget styling with the `Theme` type, but we hope to soon.
    type Style: Any + PartialEq + ::std::fmt::Debug;

    /// Return the kind of the widget as a &'static str. Note that this must be unique from all
    /// other widgets' "unique kinds". This is used by conrod to help avoid UiId errors.
    fn unique_kind(&self) -> &'static str;

    /// Return the initial `State` of the Widget. The `Ui` will only call this once.
    fn init_state(&self) -> Self::State;

    /// Return the styling of the widget. The `Ui` will call this once prior to each `update`. It
    /// does this so that it can check for differences in `Style` in case a new `Element` needs to
    /// be constructed.
    fn style(&self) -> Self::Style;

    /// Your widget's previous state is given to you as a parameter and it is your job to
    /// construct and return an Update that will be used to update the widget's cached state.
    ///
    /// # Arguments
    /// * prev - The previous state of the Widget. If none existed, `Widget::init_state` will be
    /// used to pass the initial state instead.
    /// * xy - The coordinates representing the middle of the widget.
    /// * dim - The dimensions of the widget.
    /// * input - A view into the current state of the user input (i.e. mouse and keyboard).
    /// * current_style - The style just produced by the `Widget::style` method.
    /// * theme - The currently active `Theme` within the `Ui`.
    /// * glyph_cache - Used for determining the size of rendered text if necessary.
    fn update<'a, C>(self,
                     prev: &State<Self::State>,
                     xy: Point,
                     dim: Dimensions,
                     input: UserInput<'a>,
                     current_style: &Self::Style,
                     theme: &Theme,
                     glyph_cache: &GlyphCache<C>) -> Option<Self::State>
        where C: CharacterCache;

    /// Construct a renderable Element from the current styling and new state. This will *only* be
    /// called on the occasion that the widget's `Style` or `State` has changed. Keep this in mind
    /// when designing your widget's `Style` and `State` types.
    ///
    /// # Arguments
    /// * new_state - The freshly produced State which contains the unique widget info necessary
    /// for rendering.
    /// * current_style - The freshly produced `Style` of the widget.
    /// * theme - The currently active `Theme` within the `Ui`.
    /// * glyph_cache - Used for determining the size of rendered text if necessary.
    fn draw<C>(new_state: &State<Self::State>,
               current_style: &Self::Style,
               theme: &Theme,
               glyph_cache: &GlyphCache<C>) -> Element
        where C: CharacterCache;

    /// Return the canvas to which the Widget will be attached, if there is one. Note that the
    /// CanvasId can also normally be inferred by the widget's `Position`, however calling this
    /// method will override this.
    fn canvas_id(&self) -> Option<CanvasId> { None }

    /// Optionally override with the case that the widget should capture the mouse.
    fn capture_mouse(_prev: &Self::State, _new: &Self::State) -> bool { false }

    /// Optionally override with the case that the widget should capture the mouse.
    fn uncapture_mouse(_prev: &Self::State, _new: &Self::State) -> bool { false }

    /// Optionally override with the case that the widget should capture the mouse.
    fn capture_keyboard(_prev: &Self::State, _new: &Self::State) -> bool { false }

    /// Optionally override with the case that the widget should capture the mouse.
    fn uncapture_keyboard(_prev: &Self::State, _new: &Self::State) -> bool { false }

    /// Note: There should be no need to override this method.
    ///
    /// After building the widget, you call this method to set its current state into the given
    /// `Ui`. More precisely, the following will occur when calling this method:
    /// - The widget's previous state and style will be retrieved.
    /// - The widget's current `Style` will be retrieved (from the `Widget::style` method).
    /// - The widget's state will be updated (using the `Widget::udpate` method).
    /// - If the widget's state or style has changed, `Widget::draw` will be called to create the
    /// new Element for rendering.
    /// - The new State, Style and Element (if there is one) will be cached within the `Ui`.
    fn set<C>(self, id: WidgetId, ui: &mut Ui<C>) where C: CharacterCache {
        let kind = self.unique_kind();
        let new_style = self.style();
        let depth = self.get_depth();
        let pos = self.get_position();
        let (h_align, v_align) = self.get_alignment(&ui.theme);
        let dim = {
            let Ui { ref theme, ref glyph_cache, .. } = *ui;
            self.get_dimensions(theme, glyph_cache)
        };
        let xy = ui.get_xy(pos, dim, h_align, v_align);

        // Collect the previous state and style or initialise both if none exist.
        let maybe_prev_state = ui::get_widget_state::<C, Self>(ui, id, kind).map(|prev|{
            let PrevState { state, style, xy, dim, depth } = prev;
            (Some(style), State { state: state, xy: xy, dim: dim, depth: depth, })
        });
        let (maybe_prev_style, prev_state) = maybe_prev_state.unwrap_or_else(|| {
            (None, State { state: self.init_state(), dim: [0.0, 0.0], xy: [0.0, 0.0], depth: 0.0 })
        });

        // Determine the id of the canvas that the widget is attached to. If not given explicitly,
        // check the positioning to retrieve the Id from there.
        let maybe_canvas_id = self.canvas_id().or_else(|| ui::canvas_from_position(ui, pos));

        // Update the widget's state.
        let maybe_new_state = {
            // Construct a UserInput for the widget.
            let user_input = ui::user_input(ui, UiId::Widget(id), maybe_canvas_id);
            let Ui { ref theme, ref glyph_cache, .. } = *ui;
            self.update(&prev_state, xy, dim, user_input, &new_style, theme, glyph_cache)
        };

        // Check for whether or not the user input needs to be captured or uncaptured.
        {
            let new_state = match maybe_new_state {
                Some(ref new_state) => new_state,
                None => &prev_state.state
            };
            if Self::capture_mouse(&prev_state.state, new_state) {
                ui::mouse_captured_by(ui, UiId::Widget(id));
            }
            if Self::uncapture_mouse(&prev_state.state, new_state) {
                ui::mouse_uncaptured_by(ui, UiId::Widget(id));
            }
            if Self::capture_keyboard(&prev_state.state, new_state) {
                ui::keyboard_captured_by(ui, UiId::Widget(id));
            }
            if Self::uncapture_keyboard(&prev_state.state, new_state) {
                ui::keyboard_uncaptured_by(ui, UiId::Widget(id));
            }
        }

        // Determine whether or not the `State` has changed.
        let (state_has_changed, new_state) = {
            match maybe_new_state {
                Some(new_state) =>
                    (true, State { dim: dim, xy: xy, depth: depth, state: new_state }),
                None => {
                    let has_changed = xy != prev_state.xy
                        || dim != prev_state.dim
                        || depth != prev_state.depth;
                    (has_changed, State { dim: dim, xy: xy, depth: depth, ..prev_state })
                },
            }
        };

        // Determine whether or not the widget's `Style` has changed.
        let style_has_changed = match maybe_prev_style {
            Some(prev_style) => prev_style != new_style,
            None => false,
        };

        // Construct the widget's element.
        let maybe_new_element = if style_has_changed || state_has_changed {
            let Ui { ref theme, ref glyph_cache, .. } = *ui;
            Some(Self::draw(&new_state, &new_style, theme, glyph_cache))
        } else {
            None
        };

        // Store the new `State` and `Style` within the cache.
        let State { state, dim, xy, depth } = new_state;
        let store: Store<Self::State, Self::Style> = Store { state: state, style: new_style };
        ui::update_widget(ui, id, maybe_canvas_id, kind, store, dim, xy, depth, maybe_new_element);
    }

}

/// Represents the unique cached state of a widget.
#[derive(PartialEq)]
pub struct State<T> {
    /// The state of the Widget.
    pub state: T,
    /// The rectangular dimensions of the Widget.
    pub dim: Dimensions,
    /// The position of the Widget given as [x, y] coordinates.
    pub xy: Point,
    /// The rendering depth for the Widget (the default is 0.0).
    pub depth: Depth,
}

/// The previous widget state to be returned by the Ui prior to a widget updating it's new state.
pub struct PrevState<W> where W: Widget {
    /// State that is unique to the widget.
    pub state: W::State,
    /// Unique styling state for the widget.
    pub style: W::Style,
    /// Previous dimensions of the Widget.
    pub dim: Dimensions,
    /// Previous position of the Widget.
    pub xy: Point,
    /// Previous rendering depth of the Widget.
    pub depth: Depth,
}

/// The state type that we'll dynamically cast to and from Any for storage within the Cache.
#[derive(Debug)]
pub struct Store<Sta, Sty>
    where
        Sta: Any  + Debug,
        Sty: Any  + Debug,
{
    pub state: Sta,
    pub style: Sty,
}

/// A widget element for storage within the Ui's `widget_cache`.
#[derive(Debug)]
pub struct Cached {
    pub maybe_state: Option<Box<Any>>,
    pub kind: &'static str,
    pub dim: Dimensions,
    pub xy: Point,
    pub depth: Depth,
    pub element: Element,
    pub has_updated: bool,
    pub maybe_canvas_id: Option<CanvasId>,
}

impl Cached {

    /// Construct an empty Widget for a vacant widget position within the Ui.
    pub fn empty() -> Cached {
        Cached {
            maybe_state: None,
            kind: "EMPTY",
            dim: [0.0, 0.0],
            xy: [0.0, 0.0],
            depth: 0.0,
            element: ::elmesque::element::empty(),
            has_updated: false,
            maybe_canvas_id: None,
        }
    }

}

<|endoftext|>


<commit_before>use std::fmt::Debug;
use std::hash::Hash;
use std::rc::Rc;

#[macro_use]
use adapton_syntax::* ;
use adapton_sigs::* ;

pub trait ListT<A:Adapton> {
    type List ;
    type Hd ;

    fn nil  (&mut A) -> Self::List ;
    fn cons (&mut A, Self::Hd, Rc<Self::List>) -> Self::List ;

    // requisite "adaptonic" constructors: `name` and `art`:
    fn name (&mut A, A::Name, Rc<Self::List>) -> Self::List ;
    fn art  (&mut A, Art<Self::List,A::Loc>) -> Self::List ;

    fn elim<Res,Nil,Cons,Name> (&mut A, &Self::List, Nil, Cons, Name) -> Res
        where Nil:FnOnce(&mut A) -> Res
        ,    Cons:FnOnce(&mut A, &Self::Hd, &Rc<Self::List>) -> Res
        ,    Name:FnOnce(&mut A, &A::Name, &Rc<Self::List>) -> Res ;

    fn fold<Res,Cons> (self:&Self, &mut A, &Self::List, Res, Cons) -> Res
        where Cons:Fn(&mut A, Res, &Self::Hd) -> Res ;

    // TODO: Add derived operations (max, min, sum, etc.)
}

// Questions:
//  - Should `Name`s always be passed by reference?
//  - Do these Fn argss for fold need to be passed in `Rc<Box<_>>`s ?

pub trait TreeT<A:Adapton> {
    type Tree ;
    type Leaf ;
    type Bin : Hash ;

    fn nil  (&mut A) -> Self::Tree ;
    fn leaf (&mut A, Self::Leaf) -> Self::Tree ;
    fn bin  (&mut A, Self::Bin, Self::Tree, Self::Tree) -> Self::Tree ;

    // requisite "adaptonic" constructors: `name` and `art`:
    fn name (&mut A, A::Name, Self::Tree, Self::Tree) -> Self::Tree ;
    fn art  (&mut A, Art<Self::Tree,A::Loc>) -> Self::Tree ;

    fn fold<Res,Leaf,Bin> (&mut A, Self::Tree, Res, Leaf, Bin) -> Res
        where Leaf:Fn(&mut A, Res, Self::Leaf) -> Res
        ,      Bin:Fn(&mut A, Res, Self::Bin ) -> Res ;

    fn fold2<Arg,Res,Leaf,Bin> (&mut A, Self::Tree, Arg, Leaf, Bin) -> Res
        where Leaf:Fn(&mut A, Arg, Self::Leaf) -> Res
        ,      Bin:Fn(&mut A, Arg, Self::Bin, Res, Res ) -> Res ;
}

#[derive(Debug,PartialEq,Eq,Hash)]
enum List<A:Adapton,Hd> {
    Nil,
    Cons(Hd,Rc<List<A,Hd>>),
    Name(A::Name,Rc<List<A,Hd>>),
    Art(Art<List<A,Hd>, A::Loc>),
}

impl<A:Adapton+Debug+PartialEq+Eq+Hash,Hd:Debug+PartialEq+Eq+Hash> ListT<A> for List<A,Hd> {
    type Hd = Hd;
    type List = List<A,Hd>;

    fn nil  (_:&mut A)                                 -> Self::List { List::Nil }
    fn cons (_:&mut A, hd:Hd, tl:Rc<Self::List>)       -> Self::List { List::Cons(hd,tl) }
    fn name (_:&mut A, nm:A::Name, tl:Rc<Self::List>)  -> Self::List { List::Name(nm, tl) }
    fn art  (_:&mut A, art:Art<List<A,Hd>,A::Loc>)     -> Self::List { List::Art(art) }

    fn elim<Res,Nil,Cons,Name> (st:&mut A, list:&Self::List, nilf:Nil, consf:Cons, namef:Name) -> Res
        where Nil:FnOnce(&mut A) -> Res
        ,    Cons:FnOnce(&mut A, &Self::Hd, &Rc<Self::List>) -> Res
        ,    Name:FnOnce(&mut A, &A::Name, &Rc<Self::List>) -> Res
    {
        panic!("")
    }
    fn fold<Res,Cons> (self:&Self, st:&mut A, list:&Self::List, res:Res, consf:Cons) -> Res
        where Cons:Fn(&mut A, Res, &Self::Hd) -> Res
    {
        match *list {
            List::Nil => res,
            List::Cons(ref hd, ref tl) => {
                let res = consf(st, res, hd) ;
                self.fold(st, &*tl, res, consf)
            },
            List::Name(_, ref tl) => self.fold(st, &*tl, res, consf),
            List::Art(ref art) => {
                let list = st.force(art) ;
                self.fold(st, &*list, res, consf)
            }
        }
    }
}

pub fn tree_of_list_rec <A:Adapton, X:Hash+Clone, T:TreeT<A,X,X>, L:ListT<A,X>>
    (st:&mut A, list:&L::List, left_tree:T::Tree)
     -> (T::Tree, L::List)
    where X=L::Hd, X=T::Bin
{
    L::elim (
        st, &list,
        /* Nil */  |st| ( T::nil(st), L::nil(st) ),
        /* Cons */ |st, hd, rest| {
            if my_hash( hd ) == 0 {
                let nil = T::nil(st) ;
                let (right_tree, rest) = tree_of_list_rec::<A,X,T,L> ( st, rest, nil ) ;
                let tree = T::bin ( st, hd, left_tree, right_tree ) ;
                tree_of_list_rec::<A,X,T,L> ( st, &rest, tree )
            }
            else {
                let c = L::cons(st, hd.clone(), rest.clone()) ;
                (left_tree, c)
            }},
        /* Name */ |st, nm, rest| {
            if my_hash( nm ) == 0 {
                let nil = T::nil(st) ;
                let (right_tree, rest) = tree_of_list_rec::<A,X,T,L> ( st, rest, nil ) ;
                // TODO: Place left_ and right_ trees into articulations, named by name.
                let tree = T::name ( st, nm.clone(), left_tree, right_tree ) ;
                tree_of_list_rec::<A,X,T,L> ( st, &rest, tree )
            }
            else {
                let c = L::name(st, nm.clone(), rest.clone()) ;
                (left_tree, c)
            }}
        )
}
<commit_msg>wrote most of generic tree_of_list conversion<commit_after>use std::fmt::Debug;
use std::hash::Hash;
use std::rc::Rc;

#[macro_use]
use adapton_syntax::* ;
use adapton_sigs::* ;

pub trait ListT<A:Adapton,Hd> {
    type List ;

    fn nil  (&mut A) -> Self::List ;
    fn cons (&mut A, Hd, Rc<Self::List>) -> Self::List ;

    // requisite "adaptonic" constructors: `name` and `art`:
    fn name (&mut A, A::Name, Rc<Self::List>) -> Self::List ;
    fn art  (&mut A, Art<Self::List,A::Loc>) -> Self::List ;

    fn elim<Res,Nil,Cons,Name> (&mut A, &Self::List, Nil, Cons, Name) -> Res
        where Nil:FnOnce(&mut A) -> Res
        ,    Cons:FnOnce(&mut A, &Hd, &Rc<Self::List>) -> Res
        ,    Name:FnOnce(&mut A, &A::Name, &Rc<Self::List>) -> Res ;

    fn fold<Res,Cons> (self:&Self, &mut A, &Self::List, Res, Cons) -> Res
        where Cons:Fn(&mut A, Res, &Hd) -> Res ;

    // TODO: Add derived operations (max, min, sum, etc.)
}

// Questions:
//  - Should `Name`s always be passed by reference?
//  - Do these Fn argss for fold need to be passed in `Rc<Box<_>>`s ?

pub trait TreeT<A:Adapton,Leaf,Bin:Hash> {
    type Tree ;

    fn nil  (&mut A) -> Self::Tree ;
    fn leaf (&mut A, Leaf) -> Self::Tree ;
    fn bin  (&mut A, Bin, Rc<Self::Tree>, Rc<Self::Tree>) -> Self::Tree ;

    // requisite "adaptonic" constructors: `name` and `art`:
    fn name (&mut A, A::Name, Rc<Self::Tree>, Rc<Self::Tree>) -> Self::Tree ;
    fn art  (&mut A, Art<Self::Tree,A::Loc>) -> Self::Tree ;

    fn fold<Res,LeafC,BinC> (&mut A, Self::Tree, Res, LeafC, BinC) -> Res
        where LeafC:Fn(&mut A, Res, Leaf) -> Res
        ,      BinC:Fn(&mut A, Res, Bin ) -> Res ;

    fn fold2<Arg,Res,LeafC,BinC> (&mut A, Self::Tree, Arg, LeafC, BinC) -> Res
        where LeafC:Fn(&mut A, Arg, Leaf) -> Res
        ,      BinC:Fn(&mut A, Arg, Bin, Res, Res ) -> Res ;
}

#[derive(Debug,PartialEq,Eq,Hash)]
enum List<A:Adapton,Hd> {
    Nil,
    Cons(Hd,Rc<List<A,Hd>>),
    Name(A::Name,Rc<List<A,Hd>>),
    Art(Art<List<A,Hd>, A::Loc>),
}

impl<A:Adapton+Debug+PartialEq+Eq+Hash,Hd:Debug+PartialEq+Eq+Hash> ListT<A,Hd> for List<A,Hd> {
    type List = List<A,Hd>;

    fn nil  (_:&mut A)                                 -> Self::List { List::Nil }
    fn cons (_:&mut A, hd:Hd, tl:Rc<Self::List>)       -> Self::List { List::Cons(hd,tl) }
    fn name (_:&mut A, nm:A::Name, tl:Rc<Self::List>)  -> Self::List { List::Name(nm, tl) }
    fn art  (_:&mut A, art:Art<List<A,Hd>,A::Loc>)     -> Self::List { List::Art(art) }

    fn elim<Res,Nil,Cons,Name> (st:&mut A, list:&Self::List, nilf:Nil, consf:Cons, namef:Name) -> Res
        where Nil:FnOnce(&mut A) -> Res
        ,    Cons:FnOnce(&mut A, &Hd, &Rc<Self::List>) -> Res
        ,    Name:FnOnce(&mut A, &A::Name, &Rc<Self::List>) -> Res
    {
        panic!("")
    }
    fn fold<Res,Cons> (self:&Self, st:&mut A, list:&Self::List, res:Res, consf:Cons) -> Res
        where Cons:Fn(&mut A, Res, &Hd) -> Res
    {
        match *list {
            List::Nil => res,
            List::Cons(ref hd, ref tl) => {
                let res = consf(st, res, hd) ;
                self.fold(st, &*tl, res, consf)
            },
            List::Name(_, ref tl) => self.fold(st, &*tl, res, consf),
            List::Art(ref art) => {
                let list = st.force(art) ;
                self.fold(st, &*list, res, consf)
            }
        }
    }
}

pub fn tree_of_list_rec <A:Adapton, X:Hash+Clone, T:TreeT<A,X,X>, L:ListT<A,X>>
    (st:&mut A, list:&Rc<L::List>, left_tree:&Rc<T::Tree>)
     -> (Rc<T::Tree>, Rc<L::List>)
{
    L::elim (
        st, &list,
        /* Nil */  |st| ( Rc::new(T::nil(st)), Rc::new(L::nil(st)) ),
        /* Cons */ |st, hd, rest| {
            if my_hash( hd ) == 0 {
                let nil = Rc::new(T::nil(st)) ;
                let (right_tree, rest) = tree_of_list_rec::<A,X,T,L> ( st, rest, &nil ) ;
                let tree = Rc::new(T::bin ( st, hd.clone(), left_tree.clone(), right_tree )) ;
                tree_of_list_rec::<A,X,T,L> ( st, &rest, &tree )
            }
            else {
                let c = Rc::new(L::cons(st, hd.clone(), rest.clone())) ;
                (left_tree.clone(), c)
            }},
        /* Name */ |st, nm, rest| {
            if my_hash( nm ) == 0 {
                let nil = Rc::new(T::nil(st)) ;
                let (right_tree, rest) = tree_of_list_rec::<A,X,T,L> ( st, rest, &nil ) ;
                // TODO: Place left_ and right_ trees into articulations, named by name.
                let tree = Rc::new(T::name( st, nm.clone(), left_tree.clone(), right_tree )) ;
                tree_of_list_rec::<A,X,T,L> ( st, &rest, &tree )
            }
            else {
                let c = Rc::new(L::name(st, nm.clone(), rest.clone())) ;
                (left_tree.clone(), c)
            }}
        )
}
<|endoftext|>


<commit_before>use std::io::net::tcp::TcpStream;
use std::io::IoResult;
use std::num;

pub enum Payload {
    Text(Box<String>),
    Binary(Vec<u8>),
}

#[deriving(FromPrimitive)]
pub enum Opcode {
    ContinuationOp = 0x0,
    TextOp         = 0x1,
    BinaryOp       = 0x2,
    CloseOp        = 0x8,
    PingOp         = 0x9,
    PongOp         = 0xA,
}

// this struct will eventually encapsulate data framing, opcodes, ws extensions, masks etc
// right now, only single frames, with a text payload are supported
pub struct Message {
    pub payload: Payload,
    pub opcode: Opcode
}

impl Message {
    pub fn load(stream: &mut TcpStream) -> IoResult<Box<Message>> {
        let vec1 = try!(stream.read_exact(2));
        let buf1 = vec1.as_slice();
        println!("buf1: {:t} {:t}", buf1[0], buf1[1]);

        //let fin    = buf1[0] & 0b1000_0000; // TODO check this, required for handling fragmented messages

        /* we ignore these, as they are only used if a websocket protocol has been enabled, and optionally at that
        let rsv1   = buf1[0] & 0b0100_0000;
        let rsv2   = buf1[0] & 0b0010_0000;
        let rsv3   = buf1[0] & 0b0001_0000;
        */
        let opcode = buf1[0] & 0b0000_1111;
        let opcode: Opcode = num::from_u8(opcode).unwrap(); // all valid RFC 6455 Opcodes specified, invalid ones will fail!()

        //let mask    = buf1[1] & 0b1000_0000; TODO use this to determine whether to unmask or not
        let pay_len = buf1[1] & 0b0111_1111;

        let payload_length = match pay_len {
            127 => try!(stream.read_be_u64()), // 8 bytes in network byte order
            126 => try!(stream.read_be_u16()) as u64, // 2 bytes in network byte order
            _   => pay_len as u64
        };
        println!("payload_length: {}", payload_length);

        let masking_key_vec = try!(stream.read_exact(4));
        let masking_key_buf = masking_key_vec.as_slice();
        println!("masking_key_buf: {:t} {:t} {:t} {:t}", masking_key_buf[0], masking_key_buf[1], masking_key_buf[2], masking_key_buf[3]);

        let masked_payload_buf = try!(stream.read_exact(payload_length as uint));

        // unmask the payload
        let mut payload_buf = vec!(); // instead of a mutable vector, a map_with_index would be nice. or maybe just mutate the existing buffer in place.
        for (i, &octet) in masked_payload_buf.iter().enumerate() {
            payload_buf.push(octet ^ masking_key_buf[i % 4]);
        }

        let payload: Payload = match opcode {
            TextOp   => Text(box String::from_utf8(payload_buf).unwrap()),
            BinaryOp => Binary(payload_buf),
            _        => unimplemented!(), // TODO ping/pong/close/continuation
        };

        let message = box Message {
            payload: payload,
            opcode: opcode
        };

        return Ok(message);
    }

    // FIXME support for clients - masking for clients, but need know whether
    // it's a client or server doing the sending. maybe a private `send` with
    // the common code, and public `client_send` and `server_send` methods.
    // these methods will be called by the WebSokcetClient and WebSocketServer
    // traits respectively, and the interface for both clients and servers is
    // the same - just send on the channel, and the trait takes care of it
    pub fn send(&self, stream: &mut TcpStream) -> IoResult<()> {
        let payload_length = match self.payload {
            Text(ref p) => p.len(),
            Binary(ref p) => p.len(),
        };

        try!(stream.write_u8(0b1000_0000 | self.opcode as u8)); // fin: 1, rsv: 000, opcode: see Opcode

        // FIXME: this assumes a server. the first bit, which is the "mask" bit, is implicitly set as 0 here, as required for ws servers
        if payload_length <= 125 {
            try!(stream.write_u8(payload_length as u8));
        } else if payload_length <= 65536 {
            try!(stream.write_u8(126));
            try!(stream.write_be_u16(payload_length as u16));
        } else if payload_length <= 65536 {
            try!(stream.write_u8(127));
            try!(stream.write_be_u64(payload_length as u64));
        }

        match self.payload {
            Text(ref p)   => try!(stream.write((*p).as_slice().as_bytes())),
            Binary(ref p) => try!(stream.write((*p).as_slice())),
        }

        try!(stream.flush());

        return Ok(());
    }
}
<commit_msg>Update message.rs<commit_after>use std::io::net::tcp::TcpStream;
use std::io::IoResult;
use std::num;

pub enum Payload {
    Text(Box<String>),
    Binary(Vec<u8>),
}

#[deriving(FromPrimitive)]
pub enum Opcode {
    ContinuationOp = 0x0,
    TextOp         = 0x1,
    BinaryOp       = 0x2,
    CloseOp        = 0x8,
    PingOp         = 0x9,
    PongOp         = 0xA,
}

// this struct will eventually encapsulate data framing, opcodes, ws extensions, masks etc
// right now, only single frames, with a text payload are supported
pub struct Message {
    pub payload: Payload,
    pub opcode: Opcode
}

impl Message {
    pub fn load(stream: &mut TcpStream) -> IoResult<Box<Message>> {
        let vec1 = try!(stream.read_exact(2));
        let buf1 = vec1.as_slice();
        println!("buf1: {:t} {:t}", buf1[0], buf1[1]);

        //let fin    = buf1[0] & 0b1000_0000; // TODO check this, required for handling fragmented messages

        /* we ignore these, as they are only used if a websocket protocol has been enabled, and optionally at that
        let rsv1   = buf1[0] & 0b0100_0000;
        let rsv2   = buf1[0] & 0b0010_0000;
        let rsv3   = buf1[0] & 0b0001_0000;
        */
        let opcode = buf1[0] & 0b0000_1111;
        let opcode: Opcode = num::from_u8(opcode).unwrap(); // all valid RFC 6455 Opcodes specified, invalid ones will fail!()

        //let mask    = buf1[1] & 0b1000_0000; TODO use this to determine whether to unmask or not
        let pay_len = buf1[1] & 0b0111_1111;

        let payload_length = match pay_len {
            127 => try!(stream.read_be_u64()), // 8 bytes in network byte order
            126 => try!(stream.read_be_u16()) as u64, // 2 bytes in network byte order
            _   => pay_len as u64
        };
        println!("payload_length: {}", payload_length);

        let masking_key_vec = try!(stream.read_exact(4));
        let masking_key_buf = masking_key_vec.as_slice();
        println!("masking_key_buf: {:t} {:t} {:t} {:t}", masking_key_buf[0], masking_key_buf[1], masking_key_buf[2], masking_key_buf[3]);

        let masked_payload_buf = try!(stream.read_exact(payload_length as uint));

        // unmask the payload
        let mut payload_buf = vec!(); // instead of a mutable vector, a map_with_index would be nice. or maybe just mutate the existing buffer in place.
        for (i, &octet) in masked_payload_buf.iter().enumerate() {
            payload_buf.push(octet ^ masking_key_buf[i % 4]);
        }

        let payload: Payload = match opcode {
            TextOp   => Text(box String::from_utf8(payload_buf).unwrap()),
            BinaryOp => Binary(payload_buf),
            _        => unimplemented!(), // TODO ping/pong/close/continuation
        };

        let message = box Message {
            payload: payload,
            opcode: opcode
        };

        return Ok(message);
    }

    // FIXME support for clients - masking for clients, but need know whether
    // it's a client or server doing the sending. maybe a private `send` with
    // the common code, and public `client_send` and `server_send` methods.
    // these methods will be called by the WebSokcetClient and WebSocketServer
    // traits respectively, and the interface for both clients and servers is
    // the same - just send on the channel, and the trait takes care of it
    pub fn send(&self, stream: &mut TcpStream) -> IoResult<()> {
        let payload_length = match self.payload {
            Text(ref p) => p.len(),
            Binary(ref p) => p.len(),
        };

        try!(stream.write_u8(0b1000_0000 | self.opcode as u8)); // fin: 1, rsv: 000, opcode: see Opcode

        // FIXME: this assumes a server. the first bit, which is the "mask" bit, is implicitly set as 0 here, as required for ws servers
        if payload_length <= 125 {
            try!(stream.write_u8(payload_length as u8));
        } else if payload_length <= 65536 {
            try!(stream.write_u8(126));
            try!(stream.write_be_u16(payload_length as u16));
        } else if payload_length > 65536 {
            try!(stream.write_u8(127));
            try!(stream.write_be_u64(payload_length as u64));
        }

        match self.payload {
            Text(ref p)   => try!(stream.write((*p).as_slice().as_bytes())),
            Binary(ref p) => try!(stream.write((*p).as_slice())),
        }

        try!(stream.flush());

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


<commit_before>use crate::data::{Id, Item, Rating};
use crate::helpers::{ElementDataRef, QuerySelector};
use html5ever::{expanded_name, local_name, namespace_url, ns};
use kuchiki::NodeRef;

fn get_item_id(elem: &ElementDataRef) -> Id {
    static ID_PREFIX: &'static str = "item_";
    let attrs = elem.attributes.borrow();
    let id = attrs.get(local_name!("id")).unwrap();
    let (prefix, id_str) = id.split_at(ID_PREFIX.len());
    assert!(prefix == ID_PREFIX);
    id_str.parse().unwrap()
}

fn get_item_title(elem: &ElementDataRef) -> String {
    let title_node = elem.query_selector("h3>*:last-child").unwrap();
    assert!(
        title_node.name.expanded() == expanded_name!(html "small")
            || title_node.name.expanded() == expanded_name!(html "a")
    );
    title_node.text_contents()
}

fn get_item_rating(elem: &ElementDataRef) -> Option<Rating> {
    static STARS_PREFIX: &'static str = "sstars";
    let elem = elem.query_selector(".starsinfo")?;
    let attrs = elem.attributes.borrow();
    let classes = attrs.get(local_name!("class")).unwrap();
    let result = classes
        .split_whitespace()
        .find_map(|class| {
            if !class.starts_with(STARS_PREFIX) {
                return None;
            }
            let rating = class[STARS_PREFIX.len()..].parse().unwrap();
            assert!(rating >= 1 && rating <= 10);
            Some(rating)
        })
        .unwrap();
    Some(result)
}

fn get_item_tags(elem: &ElementDataRef) -> Vec<String> {
    static TAGS_PREFIX: &'static str = "标签: ";
    if let Some(tags_elem) = elem.query_selector(".collectInfo>.tip") {
        let all_text = tags_elem.text_contents();
        let tag_text = all_text.trim();
        assert!(tag_text.starts_with(TAGS_PREFIX));
        tag_text[TAGS_PREFIX.len()..]
            .split(" ")
            .filter_map(|s| {
                if !s.is_empty() {
                    Some(s.to_string())
                } else {
                    None
                }
            })
            .collect()
    } else {
        vec![]
    }
}

fn generate_item_from_node(elem: &ElementDataRef) -> Item {
    Item {
        id: get_item_id(elem),
        title: get_item_title(elem),
        rating: get_item_rating(elem),
        tags: get_item_tags(elem),
    }
}

pub fn get_all_items(html: NodeRef) -> Vec<Item> {
    html.select("#browserItemList>li")
        .unwrap()
        .map(|elem| generate_item_from_node(&elem))
        .collect()
}
<commit_msg>Update to handle new version<commit_after>use crate::data::{Id, Item, Rating};
use crate::helpers::{ElementDataRef, QuerySelector};
use html5ever::{expanded_name, local_name, namespace_url, ns};
use kuchiki::NodeRef;

fn get_item_id(elem: &ElementDataRef) -> Id {
    static ID_PREFIX: &'static str = "item_";
    let attrs = elem.attributes.borrow();
    let id = attrs.get(local_name!("id")).unwrap();
    let (prefix, id_str) = id.split_at(ID_PREFIX.len());
    assert!(prefix == ID_PREFIX);
    id_str.parse().unwrap()
}

fn get_item_title(elem: &ElementDataRef) -> String {
    let title_node = elem.query_selector("h3>*:last-child").unwrap();
    assert!(
        title_node.name.expanded() == expanded_name!(html "small")
            || title_node.name.expanded() == expanded_name!(html "a")
    );
    title_node.text_contents()
}

fn get_item_rating(elem: &ElementDataRef) -> Option<Rating> {
    static STARS_PREFIX: &'static str = "stars";
    let elem = elem.query_selector(".starlight")?;
    let attrs = elem.attributes.borrow();
    let classes = attrs.get(local_name!("class")).unwrap();
    let result = classes
        .split_whitespace()
        .find_map(|class| {
            if !class.starts_with(STARS_PREFIX) {
                return None;
            }
            let rating = class[STARS_PREFIX.len()..].parse().unwrap();
            assert!(rating >= 1 && rating <= 10);
            Some(rating)
        })
        .unwrap();
    Some(result)
}

fn get_item_tags(elem: &ElementDataRef) -> Vec<String> {
    static TAGS_PREFIX: &'static str = "标签: ";
    if let Some(tags_elem) = elem.query_selector(".collectInfo>.tip") {
        let all_text = tags_elem.text_contents();
        let tag_text = all_text.trim();
        assert!(tag_text.starts_with(TAGS_PREFIX));
        tag_text[TAGS_PREFIX.len()..]
            .split(" ")
            .filter_map(|s| {
                if !s.is_empty() {
                    Some(s.to_string())
                } else {
                    None
                }
            })
            .collect()
    } else {
        vec![]
    }
}

fn generate_item_from_node(elem: &ElementDataRef) -> Item {
    Item {
        id: get_item_id(elem),
        title: get_item_title(elem),
        rating: get_item_rating(elem),
        tags: get_item_tags(elem),
    }
}

pub fn get_all_items(html: NodeRef) -> Vec<Item> {
    html.select("#browserItemList>li")
        .unwrap()
        .map(|elem| generate_item_from_node(&elem))
        .collect()
}
<|endoftext|>


<commit_before>//! A simple adaptive threadpool that returns a oneshot future.

use std::{
    collections::VecDeque,
    sync::{Arc, Once},
    time::{Duration, Instant},
};

use parking_lot::{Condvar, Mutex};

use crate::{debug_delay, Lazy, OneShot, Result};

static BLOCKING_QUEUE: Lazy<Queue, fn() -> Queue> = Lazy::new(Default::default);
static IO_QUEUE: Lazy<Queue, fn() -> Queue> = Lazy::new(Default::default);
static SNAPSHOT_QUEUE: Lazy<Queue, fn() -> Queue> = Lazy::new(Default::default);

type Work = Box<dyn FnOnce() + Send + 'static>;

#[derive(Default)]
struct Queue {
    cv: Condvar,
    mu: Mutex<VecDeque<Work>>,
}

#[allow(unsafe_code)]
unsafe impl Send for Queue {}

impl Queue {
    fn recv_timeout(&self, duration: Duration) -> Option<(Work, usize)> {
        let mut queue = self.mu.lock();

        let cutoff = Instant::now() + duration;

        while queue.is_empty() {
            let res = self.cv.wait_until(&mut queue, cutoff);
            if res.timed_out() {
                break;
            }
        }

        queue.pop_front().map(|w| (w, queue.len()))
    }

    fn send(&self, work: Work) -> usize {
        let mut queue = self.mu.lock();
        queue.push_back(work);

        let len = queue.len();

        // having held the mutex makes this linearized
        // with the notify below.
        drop(queue);

        self.cv.notify_all();

        len
    }

    fn perform_work(&'static self, elastic: bool, temporary: bool) {
        let wait_limit = Duration::from_millis(100);

        let mut last_employed = Instant::now();
        while !temporary || last_employed.elapsed() < Duration::from_secs(5) {
            let guard = crate::pin();
            guard.flush();
            drop(guard);
            debug_delay();
            let task_opt = self.recv_timeout(wait_limit);

            if let Some((task, outstanding_work)) = task_opt {
                // spin up some help if we're falling behind
                if elastic && outstanding_work > 5 {
                    let spawn_res = std::thread::Builder::new()
                        .name("sled-temporary-thread".into())
                        .spawn(move || self.perform_work(false, true));
                    if let Err(e) = spawn_res {
                        log::error!(
                            "failed to spin-up temporary work thread: {:?}",
                            e
                        );
                    }
                }

                // execute the work sent to this thread
                (task)();

                last_employed = Instant::now();
            }
        }
    }
}

/// Spawn a function on the threadpool.
pub fn spawn<F, R>(work: F) -> Result<OneShot<R>>
where
    F: FnOnce() -> R + Send + 'static,
    R: Send + 'static + Sized,
{
    spawn_to(work, &BLOCKING_QUEUE)
}

#[cfg(all(
    not(miri),
    any(
        windows,
        target_os = "linux",
        target_os = "macos",
        target_os = "dragonfly",
        target_os = "freebsd",
        target_os = "openbsd",
        target_os = "netbsd",
    )
))]
fn spawn_to<F, R>(work: F, queue: &'static Queue) -> Result<OneShot<R>>
where
    F: FnOnce() -> R + Send + 'static,
    R: Send + 'static + Sized,
{
    static START_THREADS: Once = Once::new();

    START_THREADS.call_once(|| {
        std::thread::Builder::new()
            .name("sled-io-thread".into())
            .spawn(|| IO_QUEUE.perform_work(true, false))
            .expect("failed to spawn critical IO thread");

        std::thread::Builder::new()
            .name("sled-blocking-thread".into())
            .spawn(|| BLOCKING_QUEUE.perform_work(true, false))
            .expect("failed to spawn critical blocking thread");

        std::thread::Builder::new()
            .name("sled-snapshot-thread".into())
            .spawn(|| SNAPSHOT_QUEUE.perform_work(false, false))
            .expect("failed to spawn critical snapshot thread");
    });

    let (promise_filler, promise) = OneShot::pair();
    let task = move || {
        promise_filler.fill((work)());
    };

    queue.send(Box::new(task));

    Ok(promise)
}

#[cfg(any(
    miri,
    not(any(
        windows,
        target_os = "linux",
        target_os = "macos",
        target_os = "dragonfly",
        target_os = "freebsd",
        target_os = "openbsd",
        target_os = "netbsd",
    ))
))]
fn spawn_to<F, R>(work: F, _: &'static Queue) -> Result<OneShot<R>>
where
    F: FnOnce() -> R + Send + 'static,
    R: Send + 'static,
{
    // Polyfill for platforms other than those we explicitly trust to
    // perform threaded work on. Just execute a task without involving threads.
    let (promise_filler, promise) = OneShot::pair();
    promise_filler.fill((work)());
    Ok(promise)
}

pub fn truncate(
    config: crate::RunningConfig,
    at: u64,
) -> Result<OneShot<Result<()>>> {
    spawn_to(
        move || {
            log::debug!("truncating file to length {}", at);
            config
                .file
                .set_len(at)
                .and_then(|_| config.file.sync_all())
                .map_err(|e| e.into())
        },
        &IO_QUEUE,
    )
}

pub fn take_fuzzy_snapshot(
    pc: crate::pagecache::PageCache,
) -> Result<OneShot<()>> {
    spawn_to(
        move || {
            if let Err(e) = pc.take_fuzzy_snapshot() {
                log::error!("failed to write snapshot: {:?}", e);
            }
        },
        &SNAPSHOT_QUEUE,
    )
}

pub(crate) fn write_to_log(
    iobuf: Arc<crate::pagecache::iobuf::IoBuf>,
    iobufs: Arc<crate::pagecache::iobuf::IoBufs>,
) -> Result<OneShot<()>> {
    spawn_to(
        move || {
            let lsn = iobuf.lsn;
            if let Err(e) = iobufs.write_to_log(iobuf) {
                log::error!(
                    "hit error while writing iobuf with lsn {}: {:?}",
                    lsn,
                    e
                );

                // store error before notifying so that waiting threads will see
                // it
                iobufs.config.set_global_error(e);

                let intervals = iobufs.intervals.lock();

                // having held the mutex makes this linearized
                // with the notify below.
                drop(intervals);

                let _notified = iobufs.interval_updated.notify_all();
            }
        },
        &IO_QUEUE,
    )
}
<commit_msg>Fix cross-compilation<commit_after>//! A simple adaptive threadpool that returns a oneshot future.

use std::sync::Arc;

use crate::{OneShot, Result};

#[cfg(all(
    not(miri),
    any(
        windows,
        target_os = "linux",
        target_os = "macos",
        target_os = "dragonfly",
        target_os = "freebsd",
        target_os = "openbsd",
        target_os = "netbsd",
    )
))]
mod queue {
    use std::{
        collections::VecDeque,
        sync::Once,
        time::{Duration, Instant},
    };

    use parking_lot::{Condvar, Mutex};

    use crate::{debug_delay, Lazy, OneShot, Result};

    pub(super) static BLOCKING_QUEUE: Lazy<Queue, fn() -> Queue> =
        Lazy::new(Default::default);
    pub(super) static IO_QUEUE: Lazy<Queue, fn() -> Queue> =
        Lazy::new(Default::default);
    pub(super) static SNAPSHOT_QUEUE: Lazy<Queue, fn() -> Queue> =
        Lazy::new(Default::default);

    type Work = Box<dyn FnOnce() + Send + 'static>;

    pub(super) fn spawn_to<F, R>(
        work: F,
        queue: &'static Queue,
    ) -> Result<OneShot<R>>
    where
        F: FnOnce() -> R + Send + 'static,
        R: Send + 'static + Sized,
    {
        static START_THREADS: Once = Once::new();

        START_THREADS.call_once(|| {
            std::thread::Builder::new()
                .name("sled-io-thread".into())
                .spawn(|| IO_QUEUE.perform_work(true, false))
                .expect("failed to spawn critical IO thread");

            std::thread::Builder::new()
                .name("sled-blocking-thread".into())
                .spawn(|| BLOCKING_QUEUE.perform_work(true, false))
                .expect("failed to spawn critical blocking thread");

            std::thread::Builder::new()
                .name("sled-snapshot-thread".into())
                .spawn(|| SNAPSHOT_QUEUE.perform_work(false, false))
                .expect("failed to spawn critical snapshot thread");
        });

        let (promise_filler, promise) = OneShot::pair();
        let task = move || {
            promise_filler.fill((work)());
        };

        queue.send(Box::new(task));

        Ok(promise)
    }

    #[derive(Default)]
    pub(super) struct Queue {
        cv: Condvar,
        mu: Mutex<VecDeque<Work>>,
    }

    #[allow(unsafe_code)]
    unsafe impl Send for Queue {}

    impl Queue {
        fn recv_timeout(&self, duration: Duration) -> Option<(Work, usize)> {
            let mut queue = self.mu.lock();

            let cutoff = Instant::now() + duration;

            while queue.is_empty() {
                let res = self.cv.wait_until(&mut queue, cutoff);
                if res.timed_out() {
                    break;
                }
            }

            queue.pop_front().map(|w| (w, queue.len()))
        }

        fn send(&self, work: Work) -> usize {
            let mut queue = self.mu.lock();
            queue.push_back(work);

            let len = queue.len();

            // having held the mutex makes this linearized
            // with the notify below.
            drop(queue);

            self.cv.notify_all();

            len
        }

        fn perform_work(&'static self, elastic: bool, temporary: bool) {
            let wait_limit = Duration::from_millis(100);

            let mut last_employed = Instant::now();
            while !temporary || last_employed.elapsed() < Duration::from_secs(5)
            {
                let guard = crate::pin();
                guard.flush();
                drop(guard);
                debug_delay();
                let task_opt = self.recv_timeout(wait_limit);

                if let Some((task, outstanding_work)) = task_opt {
                    // spin up some help if we're falling behind
                    if elastic && outstanding_work > 5 {
                        let spawn_res = std::thread::Builder::new()
                            .name("sled-temporary-thread".into())
                            .spawn(move || self.perform_work(false, true));
                        if let Err(e) = spawn_res {
                            log::error!(
                                "failed to spin-up temporary work thread: {:?}",
                                e
                            );
                        }
                    }

                    // execute the work sent to this thread
                    (task)();

                    last_employed = Instant::now();
                }
            }
        }
    }
}

/// Spawn a function on the threadpool.
pub fn spawn<F, R>(work: F) -> Result<OneShot<R>>
where
    F: FnOnce() -> R + Send + 'static,
    R: Send + 'static + Sized,
{
    spawn_to(work, &queue::BLOCKING_QUEUE)
}

#[cfg(any(
    miri,
    not(any(
        windows,
        target_os = "linux",
        target_os = "macos",
        target_os = "dragonfly",
        target_os = "freebsd",
        target_os = "openbsd",
        target_os = "netbsd",
    ))
))]
mod queue {
    /// This is the polyfill that just executes things synchronously.
    use crate::{OneShot, Result};

    pub(super) fn spawn_to<F, R>(work: F, _: &()) -> Result<OneShot<R>>
    where
        F: FnOnce() -> R + Send + 'static,
        R: Send + 'static,
    {
        // Polyfill for platforms other than those we explicitly trust to
        // perform threaded work on. Just execute a task without involving threads.
        let (promise_filler, promise) = OneShot::pair();
        promise_filler.fill((work)());
        Ok(promise)
    }

    pub(super) const IO_QUEUE: () = ();
    pub(super) const BLOCKING_QUEUE: () = ();
    pub(super) const SNAPSHOT_QUEUE: () = ();
}

use queue::spawn_to;

pub fn truncate(
    config: crate::RunningConfig,
    at: u64,
) -> Result<OneShot<Result<()>>> {
    spawn_to(
        move || {
            log::debug!("truncating file to length {}", at);
            config
                .file
                .set_len(at)
                .and_then(|_| config.file.sync_all())
                .map_err(|e| e.into())
        },
        &queue::IO_QUEUE,
    )
}

pub fn take_fuzzy_snapshot(
    pc: crate::pagecache::PageCache,
) -> Result<OneShot<()>> {
    spawn_to(
        move || {
            if let Err(e) = pc.take_fuzzy_snapshot() {
                log::error!("failed to write snapshot: {:?}", e);
            }
        },
        &queue::SNAPSHOT_QUEUE,
    )
}

pub(crate) fn write_to_log(
    iobuf: Arc<crate::pagecache::iobuf::IoBuf>,
    iobufs: Arc<crate::pagecache::iobuf::IoBufs>,
) -> Result<OneShot<()>> {
    spawn_to(
        move || {
            let lsn = iobuf.lsn;
            if let Err(e) = iobufs.write_to_log(iobuf) {
                log::error!(
                    "hit error while writing iobuf with lsn {}: {:?}",
                    lsn,
                    e
                );

                // store error before notifying so that waiting threads will see
                // it
                iobufs.config.set_global_error(e);

                let intervals = iobufs.intervals.lock();

                // having held the mutex makes this linearized
                // with the notify below.
                drop(intervals);

                let _notified = iobufs.interval_updated.notify_all();
            }
        },
        &queue::IO_QUEUE,
    )
}
<|endoftext|>


<commit_before>//! A simple adaptive threadpool that returns a oneshot future.

use std::{
    collections::VecDeque,
    sync::atomic::{AtomicBool, Ordering::Relaxed},
    thread,
    time::{Duration, Instant},
};

use parking_lot::{Condvar, Mutex};

use crate::{
    debug_delay, warn, Acquire, AtomicUsize, Error, Lazy, OneShot, Result,
    SeqCst,
};

// This is lower for CI reasons.
#[cfg(windows)]
const MAX_THREADS: usize = 16;

#[cfg(not(windows))]
const MAX_THREADS: usize = 128;

const DESIRED_WAITING_THREADS: usize = 7;

static WAITING_THREAD_COUNT: AtomicUsize = AtomicUsize::new(0);
static TOTAL_THREAD_COUNT: AtomicUsize = AtomicUsize::new(0);
static SPAWNS: AtomicUsize = AtomicUsize::new(0);
static SPAWNING: AtomicBool = AtomicBool::new(false);

macro_rules! once {
    ($args:block) => {
        #[allow(clippy::items_after_statements)]
        static __E: AtomicBool = AtomicBool::new(false);
        if !__E.compare_and_swap(false, true, Relaxed) {
            // only execute this once
            $args;
        }
    };
}

type Work = Box<dyn FnOnce() + Send + 'static>;

struct Queue {
    cv: Condvar,
    mu: Mutex<VecDeque<Work>>,
}

impl Queue {
    fn recv_timeout(&self, duration: Duration) -> Option<Work> {
        let mut queue = self.mu.lock();

        let cutoff = Instant::now() + duration;

        while queue.is_empty() {
            WAITING_THREAD_COUNT.fetch_add(1, SeqCst);
            let res = self.cv.wait_until(&mut queue, cutoff);
            WAITING_THREAD_COUNT.fetch_sub(1, SeqCst);
            if res.timed_out() {
                break;
            }
        }

        queue.pop_front()
    }

    fn try_recv(&self) -> Option<Work> {
        let mut queue = self.mu.lock();
        queue.pop_front()
    }

    fn send(&self, work: Work) -> usize {
        let mut queue = self.mu.lock();
        queue.push_back(work);

        let len = queue.len();

        // having held the mutex makes this linearized
        // with the notify below.
        drop(queue);

        self.cv.notify_all();

        len
    }
}

static QUEUE: Lazy<Queue, fn() -> Queue> = Lazy::new(init_queue);
static BROKEN: AtomicBool = AtomicBool::new(false);

fn init_queue() -> Queue {
    debug_delay();
    for _ in 0..DESIRED_WAITING_THREADS {
        debug_delay();
        if let Err(e) = spawn_new_thread(true) {
            log::error!("failed to initialize threadpool: {:?}", e);
        }
    }
    Queue { cv: Condvar::new(), mu: Mutex::new(VecDeque::new()) }
}

fn perform_work(is_immortal: bool) {
    let wait_limit = Duration::from_secs(1);

    let mut performed = 0;
    let mut contiguous_overshoots = 0;

    while is_immortal || performed < 5 || contiguous_overshoots < 3 {
        debug_delay();
        let task_res = QUEUE.recv_timeout(wait_limit);

        if let Some(task) = task_res {
            WAITING_THREAD_COUNT.fetch_sub(1, SeqCst);
            (task)();
            WAITING_THREAD_COUNT.fetch_add(1, SeqCst);
            performed += 1;
        }

        while let Some(task) = QUEUE.try_recv() {
            debug_delay();
            WAITING_THREAD_COUNT.fetch_sub(1, SeqCst);
            (task)();
            WAITING_THREAD_COUNT.fetch_add(1, SeqCst);
            performed += 1;
        }

        debug_delay();

        let waiting = WAITING_THREAD_COUNT.load(Acquire);

        if waiting > DESIRED_WAITING_THREADS {
            contiguous_overshoots += 1;
        } else {
            contiguous_overshoots = 0;
        }
    }
}

// Create up to MAX_THREADS dynamic blocking task worker threads.
// Dynamic threads will terminate themselves if they don't
// receive any work after one second.
fn maybe_spawn_new_thread() -> Result<()> {
    debug_delay();
    let total_workers = TOTAL_THREAD_COUNT.load(Acquire);
    debug_delay();
    let waiting_threads = WAITING_THREAD_COUNT.load(Acquire);

    if waiting_threads >= DESIRED_WAITING_THREADS
        || total_workers >= MAX_THREADS
    {
        return Ok(());
    }

    if !SPAWNING.compare_and_swap(false, true, SeqCst) {
        spawn_new_thread(false)?;
    }

    Ok(())
}

fn spawn_new_thread(is_immortal: bool) -> Result<()> {
    if BROKEN.load(Relaxed) {
        return Err(Error::ReportableBug(
            "IO thread unexpectedly panicked. please report \
            this bug on the sled github repo."
                .to_string(),
        ));
    }

    let spawn_id = SPAWNS.fetch_add(1, SeqCst);

    TOTAL_THREAD_COUNT.fetch_add(1, SeqCst);
    let spawn_res = thread::Builder::new()
        .name(format!("sled-io-{}", spawn_id))
        .spawn(move || {
            SPAWNING.store(false, SeqCst);
            debug_delay();
            let res = std::panic::catch_unwind(|| perform_work(is_immortal));
            TOTAL_THREAD_COUNT.fetch_sub(1, SeqCst);
            if is_immortal || res.is_err() {
                // IO thread panicked, shut down the system
                log::error!(
                    "IO thread unexpectedly terminated.
                    please report this error at the sled github repo. {:?}",
                    res
                );
                BROKEN.store(true, SeqCst);
            }
        });

    if let Err(e) = spawn_res {
        SPAWNING.store(false, SeqCst);
        once!({
            warn!(
                "Failed to dynamically increase the threadpool size: {:?}.",
                e,
            )
        });
    }

    Ok(())
}

/// Spawn a function on the threadpool.
pub fn spawn<F, R>(work: F) -> Result<OneShot<R>>
where
    F: FnOnce() -> R + Send + 'static,
    R: Send + 'static + Sized,
{
    let (promise_filler, promise) = OneShot::pair();
    let task = move || {
        let result = (work)();
        promise_filler.fill(result);
    };

    let depth = QUEUE.send(Box::new(task));

    if depth > DESIRED_WAITING_THREADS {
        maybe_spawn_new_thread()?;
    }

    Ok(promise)
}
<commit_msg>Remove macro definition that's only used once<commit_after>//! A simple adaptive threadpool that returns a oneshot future.

use std::{
    collections::VecDeque,
    sync::atomic::{AtomicBool, Ordering::Relaxed},
    thread,
    time::{Duration, Instant},
};

use parking_lot::{Condvar, Mutex};

use crate::{
    debug_delay, warn, Acquire, AtomicUsize, Error, Lazy, OneShot, Result,
    SeqCst,
};

// This is lower for CI reasons.
#[cfg(windows)]
const MAX_THREADS: usize = 16;

#[cfg(not(windows))]
const MAX_THREADS: usize = 128;

const DESIRED_WAITING_THREADS: usize = 7;

static WAITING_THREAD_COUNT: AtomicUsize = AtomicUsize::new(0);
static TOTAL_THREAD_COUNT: AtomicUsize = AtomicUsize::new(0);
static SPAWNS: AtomicUsize = AtomicUsize::new(0);
static SPAWNING: AtomicBool = AtomicBool::new(false);

type Work = Box<dyn FnOnce() + Send + 'static>;

struct Queue {
    cv: Condvar,
    mu: Mutex<VecDeque<Work>>,
}

impl Queue {
    fn recv_timeout(&self, duration: Duration) -> Option<Work> {
        let mut queue = self.mu.lock();

        let cutoff = Instant::now() + duration;

        while queue.is_empty() {
            WAITING_THREAD_COUNT.fetch_add(1, SeqCst);
            let res = self.cv.wait_until(&mut queue, cutoff);
            WAITING_THREAD_COUNT.fetch_sub(1, SeqCst);
            if res.timed_out() {
                break;
            }
        }

        queue.pop_front()
    }

    fn try_recv(&self) -> Option<Work> {
        let mut queue = self.mu.lock();
        queue.pop_front()
    }

    fn send(&self, work: Work) -> usize {
        let mut queue = self.mu.lock();
        queue.push_back(work);

        let len = queue.len();

        // having held the mutex makes this linearized
        // with the notify below.
        drop(queue);

        self.cv.notify_all();

        len
    }
}

static QUEUE: Lazy<Queue, fn() -> Queue> = Lazy::new(init_queue);
static BROKEN: AtomicBool = AtomicBool::new(false);

fn init_queue() -> Queue {
    debug_delay();
    for _ in 0..DESIRED_WAITING_THREADS {
        debug_delay();
        if let Err(e) = spawn_new_thread(true) {
            log::error!("failed to initialize threadpool: {:?}", e);
        }
    }
    Queue { cv: Condvar::new(), mu: Mutex::new(VecDeque::new()) }
}

fn perform_work(is_immortal: bool) {
    let wait_limit = Duration::from_secs(1);

    let mut performed = 0;
    let mut contiguous_overshoots = 0;

    while is_immortal || performed < 5 || contiguous_overshoots < 3 {
        debug_delay();
        let task_res = QUEUE.recv_timeout(wait_limit);

        if let Some(task) = task_res {
            WAITING_THREAD_COUNT.fetch_sub(1, SeqCst);
            (task)();
            WAITING_THREAD_COUNT.fetch_add(1, SeqCst);
            performed += 1;
        }

        while let Some(task) = QUEUE.try_recv() {
            debug_delay();
            WAITING_THREAD_COUNT.fetch_sub(1, SeqCst);
            (task)();
            WAITING_THREAD_COUNT.fetch_add(1, SeqCst);
            performed += 1;
        }

        debug_delay();

        let waiting = WAITING_THREAD_COUNT.load(Acquire);

        if waiting > DESIRED_WAITING_THREADS {
            contiguous_overshoots += 1;
        } else {
            contiguous_overshoots = 0;
        }
    }
}

// Create up to MAX_THREADS dynamic blocking task worker threads.
// Dynamic threads will terminate themselves if they don't
// receive any work after one second.
fn maybe_spawn_new_thread() -> Result<()> {
    debug_delay();
    let total_workers = TOTAL_THREAD_COUNT.load(Acquire);
    debug_delay();
    let waiting_threads = WAITING_THREAD_COUNT.load(Acquire);

    if waiting_threads >= DESIRED_WAITING_THREADS
        || total_workers >= MAX_THREADS
    {
        return Ok(());
    }

    if !SPAWNING.compare_and_swap(false, true, SeqCst) {
        spawn_new_thread(false)?;
    }

    Ok(())
}

fn spawn_new_thread(is_immortal: bool) -> Result<()> {
    if BROKEN.load(Relaxed) {
        return Err(Error::ReportableBug(
            "IO thread unexpectedly panicked. please report \
            this bug on the sled github repo."
                .to_string(),
        ));
    }

    let spawn_id = SPAWNS.fetch_add(1, SeqCst);

    TOTAL_THREAD_COUNT.fetch_add(1, SeqCst);
    let spawn_res = thread::Builder::new()
        .name(format!("sled-io-{}", spawn_id))
        .spawn(move || {
            SPAWNING.store(false, SeqCst);
            debug_delay();
            let res = std::panic::catch_unwind(|| perform_work(is_immortal));
            TOTAL_THREAD_COUNT.fetch_sub(1, SeqCst);
            if is_immortal || res.is_err() {
                // IO thread panicked, shut down the system
                log::error!(
                    "IO thread unexpectedly terminated.
                    please report this error at the sled github repo. {:?}",
                    res
                );
                BROKEN.store(true, SeqCst);
            }
        });

    if let Err(e) = spawn_res {
        static E: AtomicBool = AtomicBool::new(false);

        SPAWNING.store(false, SeqCst);

        if !E.compare_and_swap(false, true, Relaxed) {
            // only execute this once
            warn!(
                "Failed to dynamically increase the threadpool size: {:?}.",
                e,
            )
        }
    }

    Ok(())
}

/// Spawn a function on the threadpool.
pub fn spawn<F, R>(work: F) -> Result<OneShot<R>>
where
    F: FnOnce() -> R + Send + 'static,
    R: Send + 'static + Sized,
{
    let (promise_filler, promise) = OneShot::pair();
    let task = move || {
        let result = (work)();
        promise_filler.fill(result);
    };

    let depth = QUEUE.send(Box::new(task));

    if depth > DESIRED_WAITING_THREADS {
        maybe_spawn_new_thread()?;
    }

    Ok(promise)
}
<|endoftext|>