<commit_before>use std::collections::{HashMap, HashSet};

use render::Renderer;
use routing::{DagHandle, Edge, GraphWatcher, NodeData, NodeHandle};
use util::unpack_f32;

type NodeMap = HashMap<NodeHandle, Node>;

#[derive(Default)]
pub struct RefRenderer {
    nodes: NodeMap,
}

struct Node {
    data: MyNodeData,
    inbound: HashSet<Edge>
}

enum MyNodeData {
    /// This node is a non-primitive effect.
    UserNode(NodeMap),
    /// This node is an instance of another DAG.
    Graph(DagHandle),
    /// Primitive Delay effect
    Delay,
    /// Primitive Constant effect.
    /// Also serves as a unit step;
    /// Returns the float value for t >= 0, else 0.
    F32Const,
    /// Primitive effect to multiply TWO input streams sample-wise.
    Multiply,
    /// Primitive effect to calculate 1/A
    MultInv,
    /// Primitive effect to calculate A%1 (i.e. A - A.floor())
    ModuloOne,
    /// This node is a DAG definition. i.e. it holds the output edges of a DAG.
    DagIO,
}

impl Renderer for RefRenderer {
    fn get_sample(&mut self, time: u64, ch: u8) -> f32 {
        // Try to find the edge that goes to -> (Null, slot=0, ch=ch)
        let root_handle = NodeHandle::toplevel();
        // empty graph is 0 = silence
        self.nodes.get(&root_handle).map_or(0f32, |node| {
            // find all edges to ([Null], slot=0, ch=ch)
            self.sum_input_to_slot(&self.nodes, node, time, 0, ch, &Vec::new()) as f32
        })
    }
}
impl RefRenderer {
    /// Get the value on an edge at a particular time
    /// When backtracking from the output, we push each Node onto the context if we enter inside of
    ///   it (i.e. if it's a nested DAG) & pop when exiting.
    fn get_value(&self, nodes: &NodeMap, edge: &Edge, time: u64, context: &Vec<(&NodeMap, NodeHandle)>) -> f64 {
        let from = edge.from_full();
        if *from.node_handle() == None {
            // Reading from one of the inputs to the top of `context`
            // TODO: we can avoid cloning by reversing the pop after recursing.
            let mut new_context = context.clone();
            let (new_nodes, head) = new_context.pop().unwrap();
            // Sum the inputs to the matching slot/ch
            self.sum_input_to_slot(new_nodes, &new_nodes[&head], time, edge.from_slot(), edge.from_ch(), &new_context)
        } else {
            // Reading from another node within the DAG
            let node = &nodes[&from];
            match node.data {
                MyNodeData::UserNode(ref new_nodes) => {
                    let mut new_context = context.clone();
                    new_context.push((nodes, from));
                    // Now find the *output* of the sub dag (or 0 if the sub dag has no outputs)
                    new_nodes.get(&NodeHandle::toplevel()).map_or(0f64, |root_node| {
                        self.sum_input_to_slot(&new_nodes, root_node, time, edge.from_slot(), edge.from_ch(), &new_context)
                    })
                },
                // Output = sum of all edges to Null of the same slot & ch, within the given DAG.
                MyNodeData::Graph(ref dag_handle) => {
                    // TODO: we can avoid cloning by reversing the push after recursing.
                    let mut new_context = context.clone();
                    new_context.push((nodes, from));
                    let subdag = &nodes[&NodeHandle::new_dag(dag_handle.clone())];
                    self.sum_input_to_slot(nodes, &subdag, time, edge.from_slot(), edge.from_ch(), &new_context)
                }
                // Output = sum of all inputs to slot 0 of the same ch.
                MyNodeData::Delay => {
                    // t<0 -> value is 0.
                    time.checked_sub(edge.from_slot() as u64).map_or(0f64, |origin_time| {
                        self.sum_input_to_slot(nodes, node, origin_time, 0, edge.from_ch(), context)
                    })
                },
                MyNodeData::F32Const => {
                    // Float value is encoded via the slot.
                    let value = edge.from_slot();
                    unpack_f32(value) as f64
                },
                MyNodeData::Multiply => {
                    // The only nonzero output is slot=0.
                    if edge.from_slot() != 0 {
                        println!("Warning: attempt to read from Multiply slot != 0");
                        0f64
                    } else {
                        // Sum all inputs from slot=0 and slot=2 into two separate
                        // variables, then multiply them.
                        let val_a = self.sum_input_to_slot(nodes, node, time, 0, edge.from_ch(), context);
                        let val_b = self.sum_input_to_slot(nodes, node, time, 1, edge.from_ch(), context);
                        val_a * val_b
                    }
                },
                MyNodeData::MultInv => {
                    // The only nonzero output is slot=0.
                    if edge.from_slot() != 0 {
                        println!("Warning: attempt to read from MultInv slot != 0");
                        0f64
                    } else {
                        // Sum all inputs
                        let val_in = self.sum_input_to_slot(nodes, node, time, 0, edge.from_ch(), context);
                        1.0f64 / val_in
                    }
                },
                MyNodeData::ModuloOne => {
                    // The only nonzero output is slot=0.
                    if edge.from_slot() != 0 {
                        println!("Warning: attempt to read from ModOne slot != 0");
                        0f64
                    } else {
                        // Sum all inputs
                        let val_in = self.sum_input_to_slot(nodes, node, time, 0, edge.from_ch(), context);
                        //val_in - val_in.floor()
                        // avoid subtracting unbounded numbers: approximate modulus via this:
                        let rem = val_in.fract();
                        if rem < 0f64 {
                            // TODO: We may be losing precision here, if rem is small.
                            // We should find a way to do true modulus.
                            rem + 1.0f64
                        } else {
                            rem
                        }
                    }
                },
                _ => panic!("Internal RefRenderer error: illegal node type"),
            }
        }
    }
    /// Return the sum of all inputs into a specific slot/channel of the given
    /// node at the given time.
    fn sum_input_to_slot(&self, nodes: &NodeMap, node: &Node, time: u64, slot: u32, ch: u8, context: &Vec<(&NodeMap, NodeHandle)>) -> f64 {
        let edges_in = node.inbound.iter().filter(|in_edge| {
            in_edge.to_slot() == slot && in_edge.to_ch() == ch
        });
        edges_in.map(|edge| self.get_value(nodes, edge, time, context)).sum()
    }

    fn make_node(&self, data: &NodeData) -> MyNodeData {
        match *data {
            NodeData::Graph(ref handle) => MyNodeData::Graph(handle.clone()),
            NodeData::Effect(ref effect) => {
                match effect.meta().get_primitive_url() {
                    Some(ref url) => {
                        match url.path() {
                            "/F32Const" => MyNodeData::F32Const,
                            "/Delay" => MyNodeData::Delay,
                            "/Multiply" => MyNodeData::Multiply,
                            "/MultInv" => MyNodeData::MultInv,
                            "/ModuloOne" => MyNodeData::ModuloOne,
                            _ => panic!("Unrecognized primitive effect: {} (full url: {})", url.path(), url),
                        }
                    }
                    None => {
                        let graph = effect.routegraph().as_ref().unwrap();
                        let mut nodes = HashMap::new();
                        for (node, data) in graph.iter_nodes() {
                            nodes.insert(node.clone(), Node::new(self.make_node(data)));
                        }
                        for edge in graph.iter_edges() {
                            nodes.entry(edge.to_full()).or_insert_with(|| {
                                Node::new(MyNodeData::DagIO)
                            }).inbound.insert(edge.clone());
                        }
                        MyNodeData::UserNode(nodes)
                    }
                }
            }
        }
    }
}

impl GraphWatcher for RefRenderer {
    fn on_add_node(&mut self, handle: &NodeHandle, data: &NodeData) {
        let my_node_data = self.make_node(data);
        self.nodes.insert(handle.clone(), Node::new(my_node_data));
        // If the node is part of a new DAG, allocate data so that future edges
        // to null within the DAG can be held.
        self.nodes.entry(NodeHandle::new_dag(handle.dag_handle().clone())).or_insert_with(|| {
            Node::new(MyNodeData::DagIO)
        });
    }
    fn on_del_node(&mut self, handle: &NodeHandle) {
        self.nodes.remove(handle);
    }
    fn on_add_edge(&mut self, edge: &Edge) {
        self.nodes.get_mut(&edge.to_full()).unwrap().inbound.insert(edge.clone());
    }
    fn on_del_edge(&mut self, edge: &Edge) {
        self.nodes.get_mut(&edge.to_full()).unwrap().inbound.remove(edge);
    }
}

impl Node {
    fn new(data: MyNodeData) -> Self {
        Node {
            data: data,
            inbound: HashSet::new(),
        }
    }
}
<commit_msg>Replace MultInv with Divide & add ModOne -> Modulo;<commit_after>use std::collections::{HashMap, HashSet};

use render::Renderer;
use routing::{DagHandle, Edge, GraphWatcher, NodeData, NodeHandle};
use util::unpack_f32;

type NodeMap = HashMap<NodeHandle, Node>;

#[derive(Default)]
pub struct RefRenderer {
    nodes: NodeMap,
}

struct Node {
    data: MyNodeData,
    inbound: HashSet<Edge>
}

enum MyNodeData {
    /// This node is a non-primitive effect.
    UserNode(NodeMap),
    /// This node is an instance of another DAG.
    Graph(DagHandle),
    /// Primitive Delay effect
    Delay,
    /// Primitive Constant effect.
    /// Also serves as a unit step;
    /// Returns the float value for t >= 0, else 0.
    F32Const,
    /// Primitive effect to multiply TWO input streams sample-wise.
    Multiply,
    /// Primitive effect to calculate A/B.
    /// Note: because not all floating point numbers have inverses,
    /// A * (1/B) != A/B. Hence, we need division (not inversion) for proper
    /// precision.
    Divide,
    /// Primitive effect to calculate A%B (true modulo; not remainder.
    /// Result, y,  is always positive: y is bounded by [0, B).
    Modulo,
    /// This node is a DAG definition. i.e. it holds the output edges of a DAG.
    DagIO,
}

impl Renderer for RefRenderer {
    fn get_sample(&mut self, time: u64, ch: u8) -> f32 {
        // Try to find the edge that goes to -> (Null, slot=0, ch=ch)
        let root_handle = NodeHandle::toplevel();
        // empty graph is 0 = silence
        self.nodes.get(&root_handle).map_or(0f32, |node| {
            // find all edges to ([Null], slot=0, ch=ch)
            self.sum_input_to_slot(&self.nodes, node, time, 0, ch, &Vec::new()) as f32
        })
    }
}
impl RefRenderer {
    /// Get the value on an edge at a particular time
    /// When backtracking from the output, we push each Node onto the context if we enter inside of
    ///   it (i.e. if it's a nested DAG) & pop when exiting.
    fn get_value(&self, nodes: &NodeMap, edge: &Edge, time: u64, context: &Vec<(&NodeMap, NodeHandle)>) -> f64 {
        let from = edge.from_full();
        if *from.node_handle() == None {
            // Reading from one of the inputs to the top of `context`
            // TODO: we can avoid cloning by reversing the pop after recursing.
            let mut new_context = context.clone();
            let (new_nodes, head) = new_context.pop().unwrap();
            // Sum the inputs to the matching slot/ch
            self.sum_input_to_slot(new_nodes, &new_nodes[&head], time, edge.from_slot(), edge.from_ch(), &new_context)
        } else {
            // Reading from another node within the DAG
            let node = &nodes[&from];
            match node.data {
                MyNodeData::UserNode(ref new_nodes) => {
                    let mut new_context = context.clone();
                    new_context.push((nodes, from));
                    // Now find the *output* of the sub dag (or 0 if the sub dag has no outputs)
                    new_nodes.get(&NodeHandle::toplevel()).map_or(0f64, |root_node| {
                        self.sum_input_to_slot(&new_nodes, root_node, time, edge.from_slot(), edge.from_ch(), &new_context)
                    })
                },
                // Output = sum of all edges to Null of the same slot & ch, within the given DAG.
                MyNodeData::Graph(ref dag_handle) => {
                    // TODO: we can avoid cloning by reversing the push after recursing.
                    let mut new_context = context.clone();
                    new_context.push((nodes, from));
                    let subdag = &nodes[&NodeHandle::new_dag(dag_handle.clone())];
                    self.sum_input_to_slot(nodes, &subdag, time, edge.from_slot(), edge.from_ch(), &new_context)
                }
                // Output = sum of all inputs to slot 0 of the same ch.
                MyNodeData::Delay => {
                    // t<0 -> value is 0.
                    time.checked_sub(edge.from_slot() as u64).map_or(0f64, |origin_time| {
                        self.sum_input_to_slot(nodes, node, origin_time, 0, edge.from_ch(), context)
                    })
                },
                MyNodeData::F32Const => {
                    // Float value is encoded via the slot.
                    let value = edge.from_slot();
                    unpack_f32(value) as f64
                },
                MyNodeData::Multiply => {
                    // The only nonzero output is slot=0.
                    if edge.from_slot() != 0 {
                        println!("Warning: attempt to read from Multiply slot != 0");
                        0f64
                    } else {
                        // Sum all inputs from slot=0 and slot=2 into two separate
                        // variables, then multiply them.
                        let val_a = self.sum_input_to_slot(nodes, node, time, 0, edge.from_ch(), context);
                        let val_b = self.sum_input_to_slot(nodes, node, time, 1, edge.from_ch(), context);
                        val_a * val_b
                    }
                },
                MyNodeData::Divide => {
                    // The only nonzero output is slot=0.
                    if edge.from_slot() != 0 {
                        println!("Warning: attempt to read from MultInv slot != 0");
                        0f64
                    } else {
                        // Sum all inputs
                        let dividend = self.sum_input_to_slot(nodes, node, time, 0, edge.from_ch(), context);
                        let divisor = self.sum_input_to_slot(nodes, node, time, 1, edge.from_ch(), context);
                        dividend / divisor
                    }
                },
                MyNodeData::Modulo => {
                    // The only nonzero output is slot=0.
                    if edge.from_slot() != 0 {
                        println!("Warning: attempt to read from Modulo slot != 0");
                        0f64
                    } else {
                        // Sum all dividends
                        let dividend = self.sum_input_to_slot(nodes, node, time, 0, edge.from_ch(), context);
                        // Sum all divisors
                        let divisor = self.sum_input_to_slot(nodes, node, time, 1, edge.from_ch(), context);
                        let rem = dividend % divisor;
                        if rem < 0f64 {
                            // TODO: We may be losing precision here, if rem is small.
                            // We should find a way to do true modulus.
                            rem + divisor
                        } else {
                            rem
                        }
                    }
                },
                _ => panic!("Internal RefRenderer error: illegal node type"),
            }
        }
    }
    /// Return the sum of all inputs into a specific slot/channel of the given
    /// node at the given time.
    fn sum_input_to_slot(&self, nodes: &NodeMap, node: &Node, time: u64, slot: u32, ch: u8, context: &Vec<(&NodeMap, NodeHandle)>) -> f64 {
        let edges_in = node.inbound.iter().filter(|in_edge| {
            in_edge.to_slot() == slot && in_edge.to_ch() == ch
        });
        edges_in.map(|edge| self.get_value(nodes, edge, time, context)).sum()
    }

    fn make_node(&self, data: &NodeData) -> MyNodeData {
        match *data {
            NodeData::Graph(ref handle) => MyNodeData::Graph(handle.clone()),
            NodeData::Effect(ref effect) => {
                match effect.meta().get_primitive_url() {
                    Some(ref url) => {
                        match url.path() {
                            "/F32Const" => MyNodeData::F32Const,
                            "/Delay" => MyNodeData::Delay,
                            "/Multiply" => MyNodeData::Multiply,
                            "/Divide" => MyNodeData::Divide,
                            "/Modulo" => MyNodeData::Modulo,
                            _ => panic!("Unrecognized primitive effect: {} (full url: {})", url.path(), url),
                        }
                    }
                    None => {
                        let graph = effect.routegraph().as_ref().unwrap();
                        let mut nodes = HashMap::new();
                        for (node, data) in graph.iter_nodes() {
                            nodes.insert(node.clone(), Node::new(self.make_node(data)));
                        }
                        for edge in graph.iter_edges() {
                            nodes.entry(edge.to_full()).or_insert_with(|| {
                                Node::new(MyNodeData::DagIO)
                            }).inbound.insert(edge.clone());
                        }
                        MyNodeData::UserNode(nodes)
                    }
                }
            }
        }
    }
}

impl GraphWatcher for RefRenderer {
    fn on_add_node(&mut self, handle: &NodeHandle, data: &NodeData) {
        let my_node_data = self.make_node(data);
        self.nodes.insert(handle.clone(), Node::new(my_node_data));
        // If the node is part of a new DAG, allocate data so that future edges
        // to null within the DAG can be held.
        self.nodes.entry(NodeHandle::new_dag(handle.dag_handle().clone())).or_insert_with(|| {
            Node::new(MyNodeData::DagIO)
        });
    }
    fn on_del_node(&mut self, handle: &NodeHandle) {
        self.nodes.remove(handle);
    }
    fn on_add_edge(&mut self, edge: &Edge) {
        self.nodes.get_mut(&edge.to_full()).unwrap().inbound.insert(edge.clone());
    }
    fn on_del_edge(&mut self, edge: &Edge) {
        self.nodes.get_mut(&edge.to_full()).unwrap().inbound.remove(edge);
    }
}

impl Node {
    fn new(data: MyNodeData) -> Self {
        Node {
            data: data,
            inbound: HashSet::new(),
        }
    }
}
<|endoftext|>


<commit_before>use super::words::{Index, Range};

fn stepped_range_numeric(mut start: isize, end: isize, step: isize) -> Option<Vec<String>> {
    return if step == 0 {
        None
    } else if start < end && step < 0 {
        None
    } else if start > end && step > 0 {
        None
    } else {
        let mut out = Vec::new();
        let cmp: fn(isize, isize) -> bool = if start < end {
            |a: isize, b: isize| -> bool { a < b }
        } else {
            |a: isize, b: isize| -> bool { a > b }
        };
        while cmp(start, end) {
            out.push(start.to_string());
            start += step;
        }
        Some(out)
    };
}

fn stepped_range_chars(mut start: u8, end: u8, step: u8) -> Option<Vec<String>> {
    if step == 0 {
        None
    } else {
        let mut out = Vec::new();
        let cmp: fn(u8, u8) -> bool = if start < end {
            |a: u8, b: u8| -> bool { a < b }
        } else {
            |a: u8, b: u8| -> bool { a > b }
        };
        let step_func: fn(u8, u8) -> u8 = if start > end {
            |cur: u8, step: u8| -> u8 { cur.wrapping_sub(step) }
        } else {
            |cur: u8, step: u8| -> u8 { cur.wrapping_add(step) }
        };
        while cmp(start, end) {
            out.push((start as char).to_string());
            start = step_func(start, step);
        }
        Some(out)
    }
}

fn numeric_range(
    start: isize,
    mut end: isize,
    step: isize,
    inclusive: bool,
) -> Option<Vec<String>> {
    if start < end {
        if inclusive {
            end += 1;
        }
        stepped_range_numeric(start, end, step)
    } else if start > end {
        if inclusive {
            end += if end <= 0 { -1 } else { 1 };
        }
        stepped_range_numeric(start, end, step)
    } else {
        Some(vec![start.to_string()])
    }
}

#[inline]
fn byte_is_valid_range(b: u8) -> bool { (b >= b'a' && b <= b'z') || (b >= b'A' && b <= b'Z') }

use std::u8;
fn char_range(start: u8, mut end: u8, step: isize, inclusive: bool) -> Option<Vec<String>> {
    if !byte_is_valid_range(start) || !byte_is_valid_range(end) {
        return None;
    }

    let char_step = {
        let v = step.checked_abs()?;
        if v > u8::MAX as isize {
            return None;
        }
        v as u8
    };

    if start < end {
        if inclusive {
            end += 1;
        }
        return stepped_range_chars(start, end, char_step);
    } else if start > end {
        if inclusive {
            end -= 1;
        }
        return stepped_range_chars(start, end, char_step);
    } else {
        return Some(vec![(start as char).to_string()]);
    }
}

fn strings_to_isizes(a: &str, b: &str) -> Option<(isize, isize)> {
    if let Ok(first) = a.parse::<isize>() {
        if let Ok(sec) = b.parse::<isize>() {
            Some((first, sec))
        } else {
            None
        }
    } else {
        None
    }
}

// In a range we allow the following syntax:
//      Exclusive nonstepped: {start..end}
//      Inclusive nonstepped: {start...end}
//      Exclusive stepped: {start..step..end}
//      Inclusive stepped: {start..step...end}
pub(crate) fn parse_range(input: &str) -> Option<Vec<String>> {
    let mut read = 0;
    let mut bytes_iterator = input.bytes();
    while let Some(byte) = bytes_iterator.next() {
        match byte {
            // can only find these as the first byte, otherwise the syntax is bad
            b'a'...b'z' | b'-' | b'A'...b'Z' if read == 0 => read += 1,
            b'0'...b'9' => read += 1,
            b'.' => {
                let first = &input[..read];
                read += 1;
                // The next byte has to be a dot to be valid range
                // syntax
                match bytes_iterator.next() {
                    Some(b'.') => read += 1,
                    _ => return None,
                }

                macro_rules! finish_char {
                    ($inclusive:expr, $end_str:expr, $step:expr) => {
                        if first.len() == 1 && $end_str.len() == 1 {
                            let start = first.as_bytes()[0];
                            let end = $end_str.as_bytes()[0];
                            return char_range(start, end, $step, $inclusive);
                        } else {
                            return None;
                        }
                    };
                }

                macro_rules! finish {
                    ($inclusive:expr, $read:expr) => {
                        let end_str = &input[$read..];
                        if let Some((start, end)) = strings_to_isizes(first, end_str) {
                            return numeric_range(
                                start,
                                end,
                                if start < end { 1 } else { -1 },
                                $inclusive,
                            );
                        } else {
                            finish_char!($inclusive, end_str, 1);
                        }
                    };
                    ($inclusive:expr, $read:expr, $step:expr) => {
                        let end_str = &input[$read..];
                        if let Some((start, end)) = strings_to_isizes(first, end_str) {
                            return numeric_range(start, end, $step, $inclusive);
                        } else {
                            finish_char!($inclusive, end_str, $step);
                        }
                    };
                }

                // if the next byte is a dot we're certain it is an inclusive
                // unstepped range otherwise it has to be [-0-9a-zA-Z]
                if let Some(b) = bytes_iterator.next() {
                    read += 1;
                    match b {
                        b'.' => {
                            // this can only be an inclusive range
                            finish!(true, read);
                        }
                        b'0'...b'9' | b'-' | b'a'...b'z' | b'A'...b'Z' => {
                            // further processing needed to find out if we're reading a step or
                            // the end of an exclusive range. Step until we find another dot or
                            // the iterator ends
                            let start = read - 1;
                            while let Some(b) = bytes_iterator.next() {
                                read += 1;
                                match b {
                                    b'.' => {
                                        // stepped range input[start..read - 1] contains the step
                                        // size
                                        let step = match (&input[start..read - 1]).parse::<isize>()
                                        {
                                            Ok(v) => v,
                                            Err(_) => return None,
                                        };
                                        // count the dots to determine inclusive/exclusive
                                        let mut dots = 1;
                                        while let Some(b) = bytes_iterator.next() {
                                            read += 1;
                                            match b {
                                                b'.' => dots += 1,
                                                _ => break,
                                            }
                                        }
                                        finish!(dots == 3, read - 1, step);
                                    }
                                    // numeric values are OK but no letters anymore
                                    b'0'...b'9' => {}
                                    // unexpected
                                    _ => return None,
                                }
                            }
                            // exhausted the iterator without finding anything new means
                            // exclusive unstepped range
                            finish!(false, start);
                        }
                        // not a valid byte for ranges
                        _ => return None,
                    }
                }
            }
            _ => break,
        }
    }
    None
}

pub(crate) fn parse_index_range(input: &str) -> Option<Range> {
    let mut bytes_iterator = input.bytes().enumerate();
    while let Some((id, byte)) = bytes_iterator.next() {
        match byte {
            b'0'...b'9' | b'-' => continue,
            b'.' => {
                let first = &input[..id];

                let mut dots = 1;
                while let Some((_, byte)) = bytes_iterator.next() {
                    if byte == b'.' {
                        dots += 1
                    } else {
                        break;
                    }
                }

                let inclusive = match dots {
                    2 => false,
                    3 => true,
                    _ => break,
                };

                let end = &input[id + dots..];

                if first.is_empty() {
                    return if end.is_empty() {
                        None
                    } else {
                        match end.parse::<isize>() {
                            Ok(end) => Some(Range::to(Index::new(end))),
                            Err(_) => None,
                        }
                    };
                } else if end.is_empty() {
                    return match first.parse::<isize>() {
                        Ok(start) => Some(Range::from(Index::new(start))),
                        Err(_) => None,
                    };
                }

                if let Ok(start) = first.parse::<isize>() {
                    if let Ok(end) = end.parse::<isize>() {
                        return Some(if inclusive {
                            Range::inclusive(Index::new(start), Index::new(end))
                        } else {
                            Range::exclusive(Index::new(start), Index::new(end))
                        });
                    }
                } else {
                    break;
                }
            }
            _ => break,
        }
    }

    None
}

#[test]
fn index_ranges() {
    let valid_cases = vec![
        (
            Range::exclusive(Index::Forward(0), Index::Forward(3)),
            "0..3",
        ),
        (
            Range::inclusive(Index::Forward(0), Index::Forward(2)),
            "0...2",
        ),
        (
            Range::inclusive(Index::Forward(2), Index::Backward(1)),
            "2...-2",
        ),
        (
            Range::inclusive(Index::Forward(0), Index::Backward(0)),
            "0...-1",
        ),
        (
            Range::exclusive(Index::Backward(2), Index::Backward(0)),
            "-3..-1",
        ),
        (Range::from(Index::Backward(2)), "-3.."),
        (Range::to(Index::Forward(5)), "..5"),
    ];

    for (range, string) in valid_cases {
        assert_eq!(Some(range), parse_index_range(string));
    }

    let invalid_cases = vec!["0..A", "3-3..42"];

    for range in invalid_cases {
        assert_eq!(None, parse_index_range(range))
    }
}

#[test]
fn range_expand() {
    assert_eq!(None, parse_range("abc"));

    let actual = parse_range("-3...3");
    let expected = Some(vec![
        "-3".to_owned(),
        "-2".to_owned(),
        "-1".to_owned(),
        "0".to_owned(),
        "1".to_owned(),
        "2".to_owned(),
        "3".to_owned(),
    ]);

    assert_eq!(actual, expected);

    let actual = parse_range("3...-3");
    let expected = Some(vec![
        "3".to_owned(),
        "2".to_owned(),
        "1".to_owned(),
        "0".to_owned(),
        "-1".to_owned(),
        "-2".to_owned(),
        "-3".to_owned(),
    ]);

    assert_eq!(actual, expected);

    let actual = parse_range("a...c");
    let expected = Some(vec!["a".to_owned(), "b".to_owned(), "c".to_owned()]);

    assert_eq!(actual, expected);

    let actual = parse_range("c...a");
    let expected = Some(vec!["c".to_owned(), "b".to_owned(), "a".to_owned()]);

    assert_eq!(actual, expected);

    let actual = parse_range("A...C");
    let expected = Some(vec!["A".to_owned(), "B".to_owned(), "C".to_owned()]);

    assert_eq!(actual, expected);

    let actual = parse_range("C...A");
    let expected = Some(vec!["C".to_owned(), "B".to_owned(), "A".to_owned()]);

    assert_eq!(actual, expected);

    let actual = parse_range("C..A");
    let expected = Some(vec!["C".to_owned(), "B".to_owned()]);
    assert_eq!(actual, expected);

    let actual = parse_range("c..a");
    let expected = Some(vec!["c".to_owned(), "b".to_owned()]);
    assert_eq!(actual, expected);

    let actual = parse_range("-3..4");
    let expected = Some(vec![
        "-3".to_owned(),
        "-2".to_owned(),
        "-1".to_owned(),
        "0".to_owned(),
        "1".to_owned(),
        "2".to_owned(),
        "3".to_owned(),
    ]);

    assert_eq!(actual, expected);

    let actual = parse_range("3..-4");
    let expected = Some(vec![
        "3".to_owned(),
        "2".to_owned(),
        "1".to_owned(),
        "0".to_owned(),
        "-1".to_owned(),
        "-2".to_owned(),
        "-3".to_owned(),
    ]);

    assert_eq!(actual, expected);

    let actual = parse_range("-3...0");
    let expected = Some(vec!["-3".into(), "-2".into(), "-1".into(), "0".into()]);
    assert_eq!(actual, expected);

    let actual = parse_range("-3..0");
    let expected = Some(vec!["-3".into(), "-2".into(), "-1".into()]);
    assert_eq!(actual, expected);
}
<commit_msg>Fix descending inclusive ranges (#729)<commit_after>use super::words::{Index, Range};

fn stepped_range_numeric(mut start: isize, end: isize, step: isize) -> Option<Vec<String>> {
    return if step == 0 {
        None
    } else if start < end && step < 0 {
        None
    } else if start > end && step > 0 {
        None
    } else {
        let mut out = Vec::new();
        let cmp: fn(isize, isize) -> bool = if start < end {
            |a: isize, b: isize| -> bool { a < b }
        } else {
            |a: isize, b: isize| -> bool { a > b }
        };
        while cmp(start, end) {
            out.push(start.to_string());
            start += step;
        }
        Some(out)
    };
}

fn stepped_range_chars(mut start: u8, end: u8, step: u8) -> Option<Vec<String>> {
    if step == 0 {
        None
    } else {
        let mut out = Vec::new();
        let cmp: fn(u8, u8) -> bool = if start < end {
            |a: u8, b: u8| -> bool { a < b }
        } else {
            |a: u8, b: u8| -> bool { a > b }
        };
        let step_func: fn(u8, u8) -> u8 = if start > end {
            |cur: u8, step: u8| -> u8 { cur.wrapping_sub(step) }
        } else {
            |cur: u8, step: u8| -> u8 { cur.wrapping_add(step) }
        };
        while cmp(start, end) {
            out.push((start as char).to_string());
            start = step_func(start, step);
        }
        Some(out)
    }
}

fn numeric_range(
    start: isize,
    mut end: isize,
    step: isize,
    inclusive: bool,
) -> Option<Vec<String>> {
    if start < end {
        if inclusive {
            end += 1;
        }
        stepped_range_numeric(start, end, step)
    } else if start > end {
        if inclusive {
            end -= 1;
        }
        stepped_range_numeric(start, end, step)
    } else {
        Some(vec![start.to_string()])
    }
}

#[inline]
fn byte_is_valid_range(b: u8) -> bool { (b >= b'a' && b <= b'z') || (b >= b'A' && b <= b'Z') }

use std::u8;
fn char_range(start: u8, mut end: u8, step: isize, inclusive: bool) -> Option<Vec<String>> {
    if !byte_is_valid_range(start) || !byte_is_valid_range(end) {
        return None;
    }

    let char_step = {
        let v = step.checked_abs()?;
        if v > u8::MAX as isize {
            return None;
        }
        v as u8
    };

    if start < end {
        if inclusive {
            end += 1;
        }
        return stepped_range_chars(start, end, char_step);
    } else if start > end {
        if inclusive {
            end -= 1;
        }
        return stepped_range_chars(start, end, char_step);
    } else {
        return Some(vec![(start as char).to_string()]);
    }
}

fn strings_to_isizes(a: &str, b: &str) -> Option<(isize, isize)> {
    if let Ok(first) = a.parse::<isize>() {
        if let Ok(sec) = b.parse::<isize>() {
            Some((first, sec))
        } else {
            None
        }
    } else {
        None
    }
}

// In a range we allow the following syntax:
//      Exclusive nonstepped: {start..end}
//      Inclusive nonstepped: {start...end}
//      Exclusive stepped: {start..step..end}
//      Inclusive stepped: {start..step...end}
pub(crate) fn parse_range(input: &str) -> Option<Vec<String>> {
    let mut read = 0;
    let mut bytes_iterator = input.bytes();
    while let Some(byte) = bytes_iterator.next() {
        match byte {
            // can only find these as the first byte, otherwise the syntax is bad
            b'a'...b'z' | b'-' | b'A'...b'Z' if read == 0 => read += 1,
            b'0'...b'9' => read += 1,
            b'.' => {
                let first = &input[..read];
                read += 1;
                // The next byte has to be a dot to be valid range
                // syntax
                match bytes_iterator.next() {
                    Some(b'.') => read += 1,
                    _ => return None,
                }

                macro_rules! finish_char {
                    ($inclusive:expr, $end_str:expr, $step:expr) => {
                        if first.len() == 1 && $end_str.len() == 1 {
                            let start = first.as_bytes()[0];
                            let end = $end_str.as_bytes()[0];
                            return char_range(start, end, $step, $inclusive);
                        } else {
                            return None;
                        }
                    };
                }

                macro_rules! finish {
                    ($inclusive:expr, $read:expr) => {
                        let end_str = &input[$read..];
                        if let Some((start, end)) = strings_to_isizes(first, end_str) {
                            return numeric_range(
                                start,
                                end,
                                if start < end { 1 } else { -1 },
                                $inclusive,
                            );
                        } else {
                            finish_char!($inclusive, end_str, 1);
                        }
                    };
                    ($inclusive:expr, $read:expr, $step:expr) => {
                        let end_str = &input[$read..];
                        if let Some((start, end)) = strings_to_isizes(first, end_str) {
                            return numeric_range(start, end, $step, $inclusive);
                        } else {
                            finish_char!($inclusive, end_str, $step);
                        }
                    };
                }

                // if the next byte is a dot we're certain it is an inclusive
                // unstepped range otherwise it has to be [-0-9a-zA-Z]
                if let Some(b) = bytes_iterator.next() {
                    read += 1;
                    match b {
                        b'.' => {
                            // this can only be an inclusive range
                            finish!(true, read);
                        }
                        b'0'...b'9' | b'-' | b'a'...b'z' | b'A'...b'Z' => {
                            // further processing needed to find out if we're reading a step or
                            // the end of an exclusive range. Step until we find another dot or
                            // the iterator ends
                            let start = read - 1;
                            while let Some(b) = bytes_iterator.next() {
                                read += 1;
                                match b {
                                    b'.' => {
                                        // stepped range input[start..read - 1] contains the step
                                        // size
                                        let step = match (&input[start..read - 1]).parse::<isize>()
                                        {
                                            Ok(v) => v,
                                            Err(_) => return None,
                                        };
                                        // count the dots to determine inclusive/exclusive
                                        let mut dots = 1;
                                        while let Some(b) = bytes_iterator.next() {
                                            read += 1;
                                            match b {
                                                b'.' => dots += 1,
                                                _ => break,
                                            }
                                        }
                                        finish!(dots == 3, read - 1, step);
                                    }
                                    // numeric values are OK but no letters anymore
                                    b'0'...b'9' => {}
                                    // unexpected
                                    _ => return None,
                                }
                            }
                            // exhausted the iterator without finding anything new means
                            // exclusive unstepped range
                            finish!(false, start);
                        }
                        // not a valid byte for ranges
                        _ => return None,
                    }
                }
            }
            _ => break,
        }
    }
    None
}

pub(crate) fn parse_index_range(input: &str) -> Option<Range> {
    let mut bytes_iterator = input.bytes().enumerate();
    while let Some((id, byte)) = bytes_iterator.next() {
        match byte {
            b'0'...b'9' | b'-' => continue,
            b'.' => {
                let first = &input[..id];

                let mut dots = 1;
                while let Some((_, byte)) = bytes_iterator.next() {
                    if byte == b'.' {
                        dots += 1
                    } else {
                        break;
                    }
                }

                let inclusive = match dots {
                    2 => false,
                    3 => true,
                    _ => break,
                };

                let end = &input[id + dots..];

                if first.is_empty() {
                    return if end.is_empty() {
                        None
                    } else {
                        match end.parse::<isize>() {
                            Ok(end) => Some(Range::to(Index::new(end))),
                            Err(_) => None,
                        }
                    };
                } else if end.is_empty() {
                    return match first.parse::<isize>() {
                        Ok(start) => Some(Range::from(Index::new(start))),
                        Err(_) => None,
                    };
                }

                if let Ok(start) = first.parse::<isize>() {
                    if let Ok(end) = end.parse::<isize>() {
                        return Some(if inclusive {
                            Range::inclusive(Index::new(start), Index::new(end))
                        } else {
                            Range::exclusive(Index::new(start), Index::new(end))
                        });
                    }
                } else {
                    break;
                }
            }
            _ => break,
        }
    }

    None
}

#[test]
fn index_ranges() {
    let valid_cases = vec![
        (
            Range::exclusive(Index::Forward(0), Index::Forward(3)),
            "0..3",
        ),
        (
            Range::inclusive(Index::Forward(0), Index::Forward(2)),
            "0...2",
        ),
        (
            Range::inclusive(Index::Forward(2), Index::Backward(1)),
            "2...-2",
        ),
        (
            Range::inclusive(Index::Forward(0), Index::Backward(0)),
            "0...-1",
        ),
        (
            Range::exclusive(Index::Backward(2), Index::Backward(0)),
            "-3..-1",
        ),
        (Range::from(Index::Backward(2)), "-3.."),
        (Range::to(Index::Forward(5)), "..5"),
    ];

    for (range, string) in valid_cases {
        assert_eq!(Some(range), parse_index_range(string));
    }

    let invalid_cases = vec!["0..A", "3-3..42"];

    for range in invalid_cases {
        assert_eq!(None, parse_index_range(range))
    }
}

#[test]
fn range_expand() {
    assert_eq!(None, parse_range("abc"));

    let actual = parse_range("-3...3");
    let expected = Some(vec![
        "-3".to_owned(),
        "-2".to_owned(),
        "-1".to_owned(),
        "0".to_owned(),
        "1".to_owned(),
        "2".to_owned(),
        "3".to_owned(),
    ]);

    assert_eq!(actual, expected);

    let actual = parse_range("3...-3");
    let expected = Some(vec![
        "3".to_owned(),
        "2".to_owned(),
        "1".to_owned(),
        "0".to_owned(),
        "-1".to_owned(),
        "-2".to_owned(),
        "-3".to_owned(),
    ]);

    assert_eq!(actual, expected);

    let actual = parse_range("a...c");
    let expected = Some(vec!["a".to_owned(), "b".to_owned(), "c".to_owned()]);

    assert_eq!(actual, expected);

    let actual = parse_range("c...a");
    let expected = Some(vec!["c".to_owned(), "b".to_owned(), "a".to_owned()]);

    assert_eq!(actual, expected);

    let actual = parse_range("A...C");
    let expected = Some(vec!["A".to_owned(), "B".to_owned(), "C".to_owned()]);

    assert_eq!(actual, expected);

    let actual = parse_range("C...A");
    let expected = Some(vec!["C".to_owned(), "B".to_owned(), "A".to_owned()]);

    assert_eq!(actual, expected);

    let actual = parse_range("C..A");
    let expected = Some(vec!["C".to_owned(), "B".to_owned()]);
    assert_eq!(actual, expected);

    let actual = parse_range("c..a");
    let expected = Some(vec!["c".to_owned(), "b".to_owned()]);
    assert_eq!(actual, expected);

    let actual = parse_range("-3..4");
    let expected = Some(vec![
        "-3".to_owned(),
        "-2".to_owned(),
        "-1".to_owned(),
        "0".to_owned(),
        "1".to_owned(),
        "2".to_owned(),
        "3".to_owned(),
    ]);

    assert_eq!(actual, expected);

    let actual = parse_range("3..-4");
    let expected = Some(vec![
        "3".to_owned(),
        "2".to_owned(),
        "1".to_owned(),
        "0".to_owned(),
        "-1".to_owned(),
        "-2".to_owned(),
        "-3".to_owned(),
    ]);

    assert_eq!(actual, expected);

    let actual = parse_range("-3...0");
    let expected = Some(vec!["-3".into(), "-2".into(), "-1".into(), "0".into()]);
    assert_eq!(actual, expected);

    let actual = parse_range("-3..0");
    let expected = Some(vec!["-3".into(), "-2".into(), "-1".into()]);
    assert_eq!(actual, expected);
}
<|endoftext|>


<commit_before>// Copyright (c) 2013-2015 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

use private::capability::ClientHook;
use private::units::*;
use message;
use Word;

pub use self::FirstSegment::{NumWords, ZeroedWords};

pub type SegmentId = u32;

#[derive(Copy)]
pub struct SegmentReader {
    pub arena : ArenaPtr,
    pub ptr : *const Word,
    pub size : WordCount32
}

unsafe impl Send for SegmentReader {}

impl SegmentReader {

    #[inline]
    pub unsafe fn get_start_ptr(&self) -> *const Word {
        self.ptr.offset(0)
    }

    #[inline]
    pub fn contains_interval(&self, from : *const Word, to : *const Word) -> bool {
        let this_begin : usize = self.ptr as usize;
        let this_end : usize = unsafe { self.ptr.offset(self.size as isize) as usize };
        return from as usize >= this_begin && to as usize <= this_end && from as usize <= to as usize;
        // TODO readLimiter
    }
}

#[derive(Copy)]
pub struct SegmentBuilder {
    pub reader : SegmentReader,
    pub id : SegmentId,
    pos : *mut Word,
}

unsafe impl Send for SegmentBuilder {}

impl SegmentBuilder {

    pub fn new(arena : *mut BuilderArena,
               id : SegmentId,
               ptr : *mut Word,
               size : WordCount32) -> SegmentBuilder {
        SegmentBuilder {
            reader : SegmentReader {
                arena : ArenaPtr::Builder(arena),
                ptr : unsafe {::std::mem::transmute(ptr)},
                size : size
            },
            id : id,
            pos : ptr,
        }
    }

    pub fn get_word_offset_to(&mut self, ptr : *mut Word) -> WordCount32 {
        let this_addr : usize = self.reader.ptr as usize;
        let ptr_addr : usize = ptr as usize;
        assert!(ptr_addr >= this_addr);
        let result = (ptr_addr - this_addr) / BYTES_PER_WORD;
        return result as u32;
    }

    #[inline]
    pub fn current_size(&self) -> WordCount32 {
        ((self.pos as usize - self.reader.ptr as usize) / BYTES_PER_WORD) as u32
    }

    #[inline]
    pub fn allocate(&mut self, amount : WordCount32) -> Option<*mut Word> {
        if amount > self.reader.size - self.current_size() {
            return None;
        } else {
            let result = self.pos;
            self.pos = unsafe { self.pos.offset(amount as isize) };
            return Some(result);
        }
    }

    #[inline]
    pub fn get_ptr_unchecked(&self, offset : WordCount32) -> *mut Word {
        unsafe {
            ::std::mem::transmute(self.reader.ptr.offset(offset as isize))
        }
    }

    #[inline]
    pub fn get_segment_id(&self) -> SegmentId { self.id }

    #[inline]
    pub fn get_arena(&self) -> *mut BuilderArena {
        match self.reader.arena {
            ArenaPtr::Builder(b) => b,
            _ => unreachable!()
        }
    }

    pub fn currently_allocated<'a>(&'a self) -> &'a [Word] {
        unsafe { ::std::slice::from_raw_parts(self.get_ptr_unchecked(0), self.current_size() as usize) }
    }
}

pub struct ReaderArena {
    //    message : *message::MessageReader<'a>,
    pub segment0 : SegmentReader,

    pub more_segments : Vec<SegmentReader>,
    //XXX should this be a map as in capnproto-c++?

    pub cap_table : Vec<Option<Box<ClientHook+Send>>>,

    pub fail_fast : bool,
}

impl ReaderArena {
    pub fn new(segments : &[&[Word]], options : message::ReaderOptions) -> Box<ReaderArena> {
        assert!(segments.len() > 0);
        let mut arena = Box::new(ReaderArena {
            segment0 : SegmentReader {
                arena : ArenaPtr::Null,
                ptr : unsafe { segments[0].get_unchecked(0) },
                size : segments[0].len() as u32
            },
            more_segments : Vec::new(),
            cap_table : Vec::new(),
            fail_fast : options.fail_fast,
        });

        let arena_ptr = ArenaPtr::Reader (&*arena);

        arena.segment0.arena = arena_ptr;

        if segments.len() > 1 {
            let mut more_segment_readers = Vec::new();
            for segment in segments[1 ..].iter() {
                let segment_reader = SegmentReader {
                    arena : arena_ptr,
                    ptr : unsafe { segment.get_unchecked(0) },
                    size : segment.len() as u32
                };
                more_segment_readers.push(segment_reader);
            }
            arena.more_segments = more_segment_readers;
        }

        arena
    }

    pub fn try_get_segment(&self, id : SegmentId) -> *const SegmentReader {
        if id == 0 {
            return &self.segment0 as *const SegmentReader;
        } else {
            unsafe { self.more_segments.get_unchecked(id as usize - 1) as *const SegmentReader }
        }
    }

    #[inline]
    pub fn init_cap_table(&mut self, cap_table : Vec<Option<Box<ClientHook+Send>>>) {
        self.cap_table = cap_table;
    }

}

pub struct BuilderArena {
    pub segment0 : SegmentBuilder,
    pub segment0_for_output : &'static [Word],
    pub more_segments : Vec<Box<SegmentBuilder>>,
    pub for_output : Vec<&'static[Word]>,
    pub allocation_strategy : message::AllocationStrategy,
    pub owned_memory : Vec<*mut Word>,
    pub next_size : u32,
    pub cap_table : Vec<Option<Box<ClientHook+Send>>>,
    pub fail_fast : bool,
}

impl Drop for BuilderArena {
    fn drop(&mut self) {
        unsafe {
            ::std::rt::heap::deallocate(::std::mem::transmute(self.segment0.reader.ptr),
                                        BYTES_PER_WORD * self.segment0.reader.size as usize,
                                        BYTES_PER_WORD);
        }
        for ref segment_builder in self.more_segments.iter() {
            unsafe {
                ::std::rt::heap::deallocate(::std::mem::transmute(segment_builder.reader.ptr),
                                            BYTES_PER_WORD * segment_builder.reader.size as usize,
                                            BYTES_PER_WORD);
            }
        }
    }
}

pub enum FirstSegment<'a> {
    NumWords(u32),
    ZeroedWords(&'a mut [Word])
}

impl BuilderArena {

    pub fn new(allocation_strategy : message::AllocationStrategy,
               first_segment : FirstSegment,
               fail_fast : bool) -> Box<BuilderArena> {
        let (first_segment, num_words, owned_memory) : (*mut Word, u32, Vec<*mut Word>) = unsafe {
            match first_segment {
                NumWords(n) => {
                    let ptr : *mut Word = ::std::mem::transmute(
                        ::std::rt::heap::allocate(BYTES_PER_WORD * n as usize,
                                                  ::std::mem::min_align_of::<Word>()));
                    if ptr.is_null() {panic!("could not allocate segment")}
                    ::std::ptr::zero_memory(ptr, n as usize);
                    (ptr, n, vec!(ptr))
                }
                ZeroedWords(w) => (w.as_mut_ptr(), w.len() as u32, Vec::new())
            }};

        let mut result = Box::new(BuilderArena {
            segment0 : SegmentBuilder {
                reader : SegmentReader {
                    ptr : first_segment as *const Word,
                    size : num_words,
                    arena : ArenaPtr::Null },
                id : 0,
                pos : first_segment,
            },
            segment0_for_output : &[],
            more_segments : Vec::new(),
            for_output : Vec::new(),
            allocation_strategy : allocation_strategy,
            owned_memory : owned_memory,
            next_size : num_words,
            cap_table : Vec::new(),
            fail_fast : fail_fast,
        });

        let arena_ptr = { let ref mut ptr = *result; ptr as *mut BuilderArena};
        result.segment0.reader.arena = ArenaPtr::Builder(arena_ptr);

        result
    }

    pub fn allocate_owned_memory(&mut self, minimum_size : WordCount32) -> (*mut Word, WordCount32) {
        let size = ::std::cmp::max(minimum_size, self.next_size);
        let new_words : *mut Word = unsafe {
            ::std::mem::transmute(::std::rt::heap::allocate(BYTES_PER_WORD * size as usize,
                                                            ::std::mem::min_align_of::<Word>())) };
        if new_words.is_null() { panic!("could not allocate a new segment.") }
        unsafe { ::std::ptr::zero_memory(new_words, size as usize) };

        self.owned_memory.push(new_words);

        match self.allocation_strategy {
            message::AllocationStrategy::GrowHeuristically => { self.next_size += size; }
            _ => { }
        }
        (new_words, size)
    }

    #[inline]
    pub fn allocate(&mut self, amount : WordCount32) -> (*mut SegmentBuilder, *mut Word) {
        unsafe {
            match self.segment0.allocate(amount) {
                Some(result) => { return ((&mut self.segment0) as *mut SegmentBuilder, result) }
                None => {}
            }

            //# Need to fall back to additional segments.

            let id = {
                let len = self.more_segments.len();
                if len == 0 { 1 }
                else {
                    let result_ptr = &mut *self.more_segments.as_mut_slice()[len-1] as *mut SegmentBuilder;
                    match self.more_segments.as_mut_slice()[len - 1].allocate(amount) {
                        Some(result) => { return (result_ptr, result) }
                        None => { len + 1 }
                    }
                }};

            let (words, size) = self.allocate_owned_memory(amount);
            let mut new_builder = Box::new(SegmentBuilder::new(self, id as u32, words, size));
            let builder_ptr = &mut *new_builder as *mut SegmentBuilder;

            self.more_segments.push(new_builder);

            (builder_ptr, (*builder_ptr).allocate(amount).unwrap() )
        }
    }

    pub fn get_segment(&mut self, id : SegmentId) -> *mut SegmentBuilder {
        if id == 0 {
            &mut self.segment0 as *mut SegmentBuilder
        } else {
            &mut *self.more_segments.as_mut_slice()[(id - 1) as usize] as *mut SegmentBuilder
        }
    }

    pub fn get_segments_for_output<'a>(&'a mut self) -> &'a [&'a [Word]] {
        unsafe {
            if self.more_segments.len() == 0 {
                self.segment0_for_output = ::std::mem::transmute(self.segment0.currently_allocated());
                ::std::slice::ref_slice(&self.segment0_for_output)
            } else {
                self.for_output = Vec::new();
                self.for_output.push(::std::slice::from_raw_parts(self.segment0.reader.ptr,
                                                                  self.segment0.reader.size as usize));

                for seg in self.more_segments.iter() {
                    self.for_output.push(::std::slice::from_raw_parts(seg.reader.ptr,
                                                                      seg.current_size() as usize))
                }

                self.for_output.as_slice()
            }
        }
    }

    pub fn get_cap_table<'a>(&'a self) -> &'a [Option<Box<ClientHook+Send>>] {
        self.cap_table.as_slice()
    }

    pub fn inject_cap(&mut self, cap : Box<ClientHook+Send>) -> u32 {
        self.cap_table.push(Some(cap));
        self.cap_table.len() as u32 - 1
    }
}

#[derive(Copy)]
pub enum ArenaPtr {
    Reader(*const ReaderArena),
    Builder(*mut BuilderArena),
    Null
}

impl ArenaPtr {
    pub fn try_get_segment(&self, id : SegmentId) -> *const SegmentReader {
        unsafe {
            match self {
                &ArenaPtr::Reader(reader) => {
                    (&*reader).try_get_segment(id)
                }
                &ArenaPtr::Builder(builder) => {
                    if id == 0 {
                        &(*builder).segment0.reader as *const SegmentReader
                    } else {
                        &(*builder).more_segments.as_slice()[id as usize - 1].reader as *const SegmentReader
                    }
                }
                &ArenaPtr::Null => {
                    panic!()
                }
            }
        }
    }

    pub fn extract_cap(&self, index : usize) -> Option<Box<ClientHook+Send>> {
        unsafe {
            match self {
                &ArenaPtr::Reader(reader) => {
                    if index < (*reader).cap_table.len() {
                        match (*reader).cap_table.as_slice()[index] {
                            Some( ref hook ) => { Some(hook.copy()) }
                            None => {
                                None
                            }
                        }
                    } else {
                        None
                    }
                }
                &ArenaPtr::Builder(builder) => {
                    if index < (*builder).cap_table.len() {
                        match (*builder).cap_table.as_slice()[index] {
                            Some( ref hook ) => { Some(hook.copy()) }
                            None => {
                                None
                            }
                        }
                    } else {
                        None
                    }
                }
                &ArenaPtr::Null => {
                    panic!();
                }
            }
        }
    }

    pub fn fail_fast(&self) -> bool {
        unsafe {
            match self {
                &ArenaPtr::Reader(reader) => {
                    (*reader).fail_fast
                }
                &ArenaPtr::Builder(builder) => {
                    (*builder).fail_fast
                }
                &ArenaPtr::Null => {
                    panic!()
                }
            }
        }
    }

}
<commit_msg>eek, ScratchSpaceMallocMessageBuilder was broken<commit_after>// Copyright (c) 2013-2015 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

use private::capability::ClientHook;
use private::units::*;
use message;
use Word;

pub use self::FirstSegment::{NumWords, ZeroedWords};

pub type SegmentId = u32;

#[derive(Copy)]
pub struct SegmentReader {
    pub arena : ArenaPtr,
    pub ptr : *const Word,
    pub size : WordCount32
}

unsafe impl Send for SegmentReader {}

impl SegmentReader {

    #[inline]
    pub unsafe fn get_start_ptr(&self) -> *const Word {
        self.ptr.offset(0)
    }

    #[inline]
    pub fn contains_interval(&self, from : *const Word, to : *const Word) -> bool {
        let this_begin : usize = self.ptr as usize;
        let this_end : usize = unsafe { self.ptr.offset(self.size as isize) as usize };
        return from as usize >= this_begin && to as usize <= this_end && from as usize <= to as usize;
        // TODO readLimiter
    }
}

#[derive(Copy)]
pub struct SegmentBuilder {
    pub reader : SegmentReader,
    pub id : SegmentId,
    pos : *mut Word,
}

unsafe impl Send for SegmentBuilder {}

impl SegmentBuilder {

    pub fn new(arena : *mut BuilderArena,
               id : SegmentId,
               ptr : *mut Word,
               size : WordCount32) -> SegmentBuilder {
        SegmentBuilder {
            reader : SegmentReader {
                arena : ArenaPtr::Builder(arena),
                ptr : unsafe {::std::mem::transmute(ptr)},
                size : size
            },
            id : id,
            pos : ptr,
        }
    }

    pub fn get_word_offset_to(&mut self, ptr : *mut Word) -> WordCount32 {
        let this_addr : usize = self.reader.ptr as usize;
        let ptr_addr : usize = ptr as usize;
        assert!(ptr_addr >= this_addr);
        let result = (ptr_addr - this_addr) / BYTES_PER_WORD;
        return result as u32;
    }

    #[inline]
    pub fn current_size(&self) -> WordCount32 {
        ((self.pos as usize - self.reader.ptr as usize) / BYTES_PER_WORD) as u32
    }

    #[inline]
    pub fn allocate(&mut self, amount : WordCount32) -> Option<*mut Word> {
        if amount > self.reader.size - self.current_size() {
            return None;
        } else {
            let result = self.pos;
            self.pos = unsafe { self.pos.offset(amount as isize) };
            return Some(result);
        }
    }

    #[inline]
    pub fn get_ptr_unchecked(&self, offset : WordCount32) -> *mut Word {
        unsafe {
            ::std::mem::transmute(self.reader.ptr.offset(offset as isize))
        }
    }

    #[inline]
    pub fn get_segment_id(&self) -> SegmentId { self.id }

    #[inline]
    pub fn get_arena(&self) -> *mut BuilderArena {
        match self.reader.arena {
            ArenaPtr::Builder(b) => b,
            _ => unreachable!()
        }
    }

    pub fn currently_allocated<'a>(&'a self) -> &'a [Word] {
        unsafe { ::std::slice::from_raw_parts(self.get_ptr_unchecked(0), self.current_size() as usize) }
    }
}

pub struct ReaderArena {
    //    message : *message::MessageReader<'a>,
    pub segment0 : SegmentReader,

    pub more_segments : Vec<SegmentReader>,
    //XXX should this be a map as in capnproto-c++?

    pub cap_table : Vec<Option<Box<ClientHook+Send>>>,

    pub fail_fast : bool,
}

impl ReaderArena {
    pub fn new(segments : &[&[Word]], options : message::ReaderOptions) -> Box<ReaderArena> {
        assert!(segments.len() > 0);
        let mut arena = Box::new(ReaderArena {
            segment0 : SegmentReader {
                arena : ArenaPtr::Null,
                ptr : unsafe { segments[0].get_unchecked(0) },
                size : segments[0].len() as u32
            },
            more_segments : Vec::new(),
            cap_table : Vec::new(),
            fail_fast : options.fail_fast,
        });

        let arena_ptr = ArenaPtr::Reader (&*arena);

        arena.segment0.arena = arena_ptr;

        if segments.len() > 1 {
            let mut more_segment_readers = Vec::new();
            for segment in segments[1 ..].iter() {
                let segment_reader = SegmentReader {
                    arena : arena_ptr,
                    ptr : unsafe { segment.get_unchecked(0) },
                    size : segment.len() as u32
                };
                more_segment_readers.push(segment_reader);
            }
            arena.more_segments = more_segment_readers;
        }

        arena
    }

    pub fn try_get_segment(&self, id : SegmentId) -> *const SegmentReader {
        if id == 0 {
            return &self.segment0 as *const SegmentReader;
        } else {
            unsafe { self.more_segments.get_unchecked(id as usize - 1) as *const SegmentReader }
        }
    }

    #[inline]
    pub fn init_cap_table(&mut self, cap_table : Vec<Option<Box<ClientHook+Send>>>) {
        self.cap_table = cap_table;
    }

}

pub struct BuilderArena {
    pub segment0 : SegmentBuilder,
    pub segment0_for_output : &'static [Word],
    pub more_segments : Vec<Box<SegmentBuilder>>,
    pub for_output : Vec<&'static[Word]>,
    pub allocation_strategy : message::AllocationStrategy,
    pub owned_memory : Vec<(*mut Word, usize)>,
    pub next_size : u32,
    pub cap_table : Vec<Option<Box<ClientHook+Send>>>,
    pub fail_fast : bool,
}

impl Drop for BuilderArena {
    fn drop(&mut self) {
        for &(ptr, size) in self.owned_memory.iter() {
            unsafe {
                ::std::rt::heap::deallocate(::std::mem::transmute(ptr),
                                            BYTES_PER_WORD * size,
                                            BYTES_PER_WORD);
            }
        }
    }
}

pub enum FirstSegment<'a> {
    NumWords(u32),
    ZeroedWords(&'a mut [Word])
}

impl BuilderArena {

    pub fn new(allocation_strategy : message::AllocationStrategy,
               first_segment : FirstSegment,
               fail_fast : bool) -> Box<BuilderArena> {
        let (first_segment, num_words, owned_memory) : (*mut Word, u32, Vec<(*mut Word, usize)>) = unsafe {
            match first_segment {
                NumWords(n) => {
                    let ptr : *mut Word = ::std::mem::transmute(
                        ::std::rt::heap::allocate(BYTES_PER_WORD * n as usize,
                                                  ::std::mem::min_align_of::<Word>()));
                    if ptr.is_null() {panic!("could not allocate segment")}
                    ::std::ptr::zero_memory(ptr, n as usize);
                    (ptr, n, vec![(ptr, n as usize)])
                }
                ZeroedWords(w) => (w.as_mut_ptr(), w.len() as u32, Vec::new())
            }};

        let mut result = Box::new(BuilderArena {
            segment0 : SegmentBuilder {
                reader : SegmentReader {
                    ptr : first_segment as *const Word,
                    size : num_words,
                    arena : ArenaPtr::Null },
                id : 0,
                pos : first_segment,
            },
            segment0_for_output : &[],
            more_segments : Vec::new(),
            for_output : Vec::new(),
            allocation_strategy : allocation_strategy,
            owned_memory : owned_memory,
            next_size : num_words,
            cap_table : Vec::new(),
            fail_fast : fail_fast,
        });

        let arena_ptr = { let ref mut ptr = *result; ptr as *mut BuilderArena};
        result.segment0.reader.arena = ArenaPtr::Builder(arena_ptr);

        result
    }

    pub fn allocate_owned_memory(&mut self, minimum_size : WordCount32) -> (*mut Word, WordCount32) {
        let size = ::std::cmp::max(minimum_size, self.next_size);
        let new_words : *mut Word = unsafe {
            ::std::mem::transmute(::std::rt::heap::allocate(BYTES_PER_WORD * size as usize,
                                                            ::std::mem::min_align_of::<Word>())) };
        if new_words.is_null() { panic!("could not allocate a new segment.") }
        unsafe { ::std::ptr::zero_memory(new_words, size as usize) };

        self.owned_memory.push((new_words, size as usize));

        match self.allocation_strategy {
            message::AllocationStrategy::GrowHeuristically => { self.next_size += size; }
            _ => { }
        }
        (new_words, size)
    }

    #[inline]
    pub fn allocate(&mut self, amount : WordCount32) -> (*mut SegmentBuilder, *mut Word) {
        unsafe {
            match self.segment0.allocate(amount) {
                Some(result) => { return ((&mut self.segment0) as *mut SegmentBuilder, result) }
                None => {}
            }

            //# Need to fall back to additional segments.

            let id = {
                let len = self.more_segments.len();
                if len == 0 { 1 }
                else {
                    let result_ptr = &mut *self.more_segments.as_mut_slice()[len-1] as *mut SegmentBuilder;
                    match self.more_segments.as_mut_slice()[len - 1].allocate(amount) {
                        Some(result) => { return (result_ptr, result) }
                        None => { len + 1 }
                    }
                }};

            let (words, size) = self.allocate_owned_memory(amount);
            let mut new_builder = Box::new(SegmentBuilder::new(self, id as u32, words, size));
            let builder_ptr = &mut *new_builder as *mut SegmentBuilder;

            self.more_segments.push(new_builder);

            (builder_ptr, (*builder_ptr).allocate(amount).unwrap() )
        }
    }

    pub fn get_segment(&mut self, id : SegmentId) -> *mut SegmentBuilder {
        if id == 0 {
            &mut self.segment0 as *mut SegmentBuilder
        } else {
            &mut *self.more_segments.as_mut_slice()[(id - 1) as usize] as *mut SegmentBuilder
        }
    }

    pub fn get_segments_for_output<'a>(&'a mut self) -> &'a [&'a [Word]] {
        unsafe {
            if self.more_segments.len() == 0 {
                self.segment0_for_output = ::std::mem::transmute(self.segment0.currently_allocated());
                ::std::slice::ref_slice(&self.segment0_for_output)
            } else {
                self.for_output = Vec::new();
                self.for_output.push(::std::slice::from_raw_parts(self.segment0.reader.ptr,
                                                                  self.segment0.reader.size as usize));

                for seg in self.more_segments.iter() {
                    self.for_output.push(::std::slice::from_raw_parts(seg.reader.ptr,
                                                                      seg.current_size() as usize))
                }

                self.for_output.as_slice()
            }
        }
    }

    pub fn get_cap_table<'a>(&'a self) -> &'a [Option<Box<ClientHook+Send>>] {
        self.cap_table.as_slice()
    }

    pub fn inject_cap(&mut self, cap : Box<ClientHook+Send>) -> u32 {
        self.cap_table.push(Some(cap));
        self.cap_table.len() as u32 - 1
    }
}

#[derive(Copy)]
pub enum ArenaPtr {
    Reader(*const ReaderArena),
    Builder(*mut BuilderArena),
    Null
}

impl ArenaPtr {
    pub fn try_get_segment(&self, id : SegmentId) -> *const SegmentReader {
        unsafe {
            match self {
                &ArenaPtr::Reader(reader) => {
                    (&*reader).try_get_segment(id)
                }
                &ArenaPtr::Builder(builder) => {
                    if id == 0 {
                        &(*builder).segment0.reader as *const SegmentReader
                    } else {
                        &(*builder).more_segments.as_slice()[id as usize - 1].reader as *const SegmentReader
                    }
                }
                &ArenaPtr::Null => {
                    panic!()
                }
            }
        }
    }

    pub fn extract_cap(&self, index : usize) -> Option<Box<ClientHook+Send>> {
        unsafe {
            match self {
                &ArenaPtr::Reader(reader) => {
                    if index < (*reader).cap_table.len() {
                        match (*reader).cap_table.as_slice()[index] {
                            Some( ref hook ) => { Some(hook.copy()) }
                            None => {
                                None
                            }
                        }
                    } else {
                        None
                    }
                }
                &ArenaPtr::Builder(builder) => {
                    if index < (*builder).cap_table.len() {
                        match (*builder).cap_table.as_slice()[index] {
                            Some( ref hook ) => { Some(hook.copy()) }
                            None => {
                                None
                            }
                        }
                    } else {
                        None
                    }
                }
                &ArenaPtr::Null => {
                    panic!();
                }
            }
        }
    }

    pub fn fail_fast(&self) -> bool {
        unsafe {
            match self {
                &ArenaPtr::Reader(reader) => {
                    (*reader).fail_fast
                }
                &ArenaPtr::Builder(builder) => {
                    (*builder).fail_fast
                }
                &ArenaPtr::Null => {
                    panic!()
                }
            }
        }
    }

}
<|endoftext|>


<commit_before>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

/*! Runtime services, including the task scheduler and I/O dispatcher

The `rt` module provides the private runtime infrastructure necessary
to support core language features like the exchange and local heap,
the garbage collector, logging, local data and unwinding. It also
implements the default task scheduler and task model. Initialization
routines are provided for setting up runtime resources in common
configurations, including that used by `rustc` when generating
executables.

It is intended that the features provided by `rt` can be factored in a
way such that the core library can be built with different 'profiles'
for different use cases, e.g. excluding the task scheduler. A number
of runtime features though are critical to the functioning of the
language and an implementation must be provided regardless of the
execution environment.

Of foremost importance is the global exchange heap, in the module
`heap`. Very little practical Rust code can be written without
access to the global heap. Unlike most of `rt` the global heap is
truly a global resource and generally operates independently of the
rest of the runtime.

All other runtime features are task-local, including the local heap,
the garbage collector, local storage, logging and the stack unwinder.

The relationship between `rt` and the rest of the core library is
not entirely clear yet and some modules will be moving into or
out of `rt` as development proceeds.

Several modules in `core` are clients of `rt`:

* `std::task` - The user-facing interface to the Rust task model.
* `std::local_data` - The interface to local data.
* `std::gc` - The garbage collector.
* `std::unstable::lang` - Miscellaneous lang items, some of which rely on `std::rt`.
* `std::cleanup` - Local heap destruction.
* `std::io` - In the future `std::io` will use an `rt` implementation.
* `std::logging`
* `std::comm`

*/

#![experimental]

// FIXME: this should not be here.
#![allow(missing_doc)]

use failure;
use rustrt;

// Reexport some of our utilities which are expected by other crates.
pub use self::util::{default_sched_threads, min_stack, running_on_valgrind};

// Reexport functionality from librustrt and other crates underneath the
// standard library which work together to create the entire runtime.
pub use alloc::{heap, libc_heap};
pub use rustrt::{task, local, mutex, exclusive, stack, args, rtio, thread};
pub use rustrt::{Stdio, Stdout, Stderr, begin_unwind, begin_unwind_fmt};
pub use rustrt::{bookkeeping, at_exit, unwind, DEFAULT_ERROR_CODE, Runtime};

// Simple backtrace functionality (to print on failure)
pub mod backtrace;

// Just stuff
mod util;

/// One-time runtime initialization.
///
/// Initializes global state, including frobbing
/// the crate's logging flags, registering GC
/// metadata, and storing the process arguments.
#[allow(experimental)]
pub fn init(argc: int, argv: *const *const u8) {
    rustrt::init(argc, argv);
    unsafe { unwind::register(failure::on_fail); }
}

/// One-time runtime cleanup.
///
/// This function is unsafe because it performs no checks to ensure that the
/// runtime has completely ceased running. It is the responsibility of the
/// caller to ensure that the runtime is entirely shut down and nothing will be
/// poking around at the internal components.
///
/// Invoking cleanup while portions of the runtime are still in use may cause
/// undefined behavior.
pub unsafe fn cleanup() {
    rustrt::cleanup();
}
<commit_msg>auto merge of #18093 : steveklabnik/rust/remove_gc_reference, r=alexcrichton<commit_after>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

/*! Runtime services, including the task scheduler and I/O dispatcher

The `rt` module provides the private runtime infrastructure necessary
to support core language features like the exchange and local heap,
logging, local data and unwinding. It also implements the default task
scheduler and task model. Initialization routines are provided for setting
up runtime resources in common configurations, including that used by
`rustc` when generating executables.

It is intended that the features provided by `rt` can be factored in a
way such that the core library can be built with different 'profiles'
for different use cases, e.g. excluding the task scheduler. A number
of runtime features though are critical to the functioning of the
language and an implementation must be provided regardless of the
execution environment.

Of foremost importance is the global exchange heap, in the module
`heap`. Very little practical Rust code can be written without
access to the global heap. Unlike most of `rt` the global heap is
truly a global resource and generally operates independently of the
rest of the runtime.

All other runtime features are task-local, including the local heap,
local storage, logging and the stack unwinder.

The relationship between `rt` and the rest of the core library is
not entirely clear yet and some modules will be moving into or
out of `rt` as development proceeds.

Several modules in `core` are clients of `rt`:

* `std::task` - The user-facing interface to the Rust task model.
* `std::local_data` - The interface to local data.
* `std::unstable::lang` - Miscellaneous lang items, some of which rely on `std::rt`.
* `std::cleanup` - Local heap destruction.
* `std::io` - In the future `std::io` will use an `rt` implementation.
* `std::logging`
* `std::comm`

*/

#![experimental]

// FIXME: this should not be here.
#![allow(missing_doc)]

use failure;
use rustrt;

// Reexport some of our utilities which are expected by other crates.
pub use self::util::{default_sched_threads, min_stack, running_on_valgrind};

// Reexport functionality from librustrt and other crates underneath the
// standard library which work together to create the entire runtime.
pub use alloc::{heap, libc_heap};
pub use rustrt::{task, local, mutex, exclusive, stack, args, rtio, thread};
pub use rustrt::{Stdio, Stdout, Stderr, begin_unwind, begin_unwind_fmt};
pub use rustrt::{bookkeeping, at_exit, unwind, DEFAULT_ERROR_CODE, Runtime};

// Simple backtrace functionality (to print on failure)
pub mod backtrace;

// Just stuff
mod util;

/// One-time runtime initialization.
///
/// Initializes global state, including frobbing
/// the crate's logging flags, registering GC
/// metadata, and storing the process arguments.
#[allow(experimental)]
pub fn init(argc: int, argv: *const *const u8) {
    rustrt::init(argc, argv);
    unsafe { unwind::register(failure::on_fail); }
}

/// One-time runtime cleanup.
///
/// This function is unsafe because it performs no checks to ensure that the
/// runtime has completely ceased running. It is the responsibility of the
/// caller to ensure that the runtime is entirely shut down and nothing will be
/// poking around at the internal components.
///
/// Invoking cleanup while portions of the runtime are still in use may cause
/// undefined behavior.
pub unsafe fn cleanup() {
    rustrt::cleanup();
}
<|endoftext|>