<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use flow::{self, AFFECTS_COUNTERS, Flow, HAS_COUNTER_AFFECTING_CHILDREN, IS_ABSOLUTELY_POSITIONED};
use std::fmt;
use std::sync::Arc;
use style::computed_values::float;
use style::dom::TRestyleDamage;
use style::properties::ComputedValues;

bitflags! {
    #[doc = "Individual layout actions that may be necessary after restyling."]
    flags RestyleDamage: u8 {
        #[doc = "Repaint the node itself."]
        #[doc = "Currently unused; need to decide how this propagates."]
        const REPAINT = 0x01,

        #[doc = "Recompute the overflow regions (bounding box of object and all descendants)."]
        #[doc = "Propagates down the flow tree because the computation is bottom-up."]
        const STORE_OVERFLOW = 0x02,

        #[doc = "Recompute intrinsic inline_sizes (minimum and preferred)."]
        #[doc = "Propagates down the flow tree because the computation is"]
        #[doc = "bottom-up."]
        const BUBBLE_ISIZES = 0x04,

        #[doc = "Recompute actual inline-sizes and block-sizes, only taking out-of-flow children \
                 into account. \
                 Propagates up the flow tree because the computation is top-down."]
        const REFLOW_OUT_OF_FLOW = 0x08,

        #[doc = "Recompute actual inline_sizes and block_sizes."]
        #[doc = "Propagates up the flow tree because the computation is"]
        #[doc = "top-down."]
        const REFLOW = 0x10,

        #[doc = "Re-resolve generated content. \
                 Propagates up the flow tree because the computation is inorder."]
        const RESOLVE_GENERATED_CONTENT = 0x20,

        #[doc = "The entire flow needs to be reconstructed."]
        const RECONSTRUCT_FLOW = 0x40
    }
}

bitflags! {
    flags SpecialRestyleDamage: u8 {
        #[doc = "If this flag is set, we need to reflow the entire document. This is more or less a \
                 temporary hack to deal with cases that we don't handle incrementally yet."]
        const REFLOW_ENTIRE_DOCUMENT = 0x01,
    }
}

impl TRestyleDamage for RestyleDamage {
    fn compute(old: Option<&Arc<ComputedValues>>, new: &ComputedValues) -> RestyleDamage { compute_damage(old, new) }

    /// Returns a bitmask that represents a flow that needs to be rebuilt and reflowed.
    ///
    /// Use this instead of `RestyleDamage::all()` because `RestyleDamage::all()` will result in
    /// unnecessary sequential resolution of generated content.
    fn rebuild_and_reflow() -> RestyleDamage {
        REPAINT | STORE_OVERFLOW | BUBBLE_ISIZES | REFLOW_OUT_OF_FLOW | REFLOW | RECONSTRUCT_FLOW
    }
}

impl RestyleDamage {
    /// Supposing a flow has the given `position` property and this damage, returns the damage that
    /// we should add to the *parent* of this flow.
    pub fn damage_for_parent(self, child_is_absolutely_positioned: bool) -> RestyleDamage {
        if child_is_absolutely_positioned {
            self & (REPAINT | STORE_OVERFLOW | REFLOW_OUT_OF_FLOW | RESOLVE_GENERATED_CONTENT)
        } else {
            self & (REPAINT | STORE_OVERFLOW | REFLOW | REFLOW_OUT_OF_FLOW |
                    RESOLVE_GENERATED_CONTENT)
        }
    }

    /// Supposing the *parent* of a flow with the given `position` property has this damage,
    /// returns the damage that we should add to this flow.
    pub fn damage_for_child(self,
                            parent_is_absolutely_positioned: bool,
                            child_is_absolutely_positioned: bool)
                            -> RestyleDamage {
        match (parent_is_absolutely_positioned, child_is_absolutely_positioned) {
            (false, true) => {
                // Absolute children are out-of-flow and therefore insulated from changes.
                //
                // FIXME(pcwalton): Au contraire, if the containing block dimensions change!
                self & REPAINT
            }
            (true, false) => {
                // Changing the position of an absolutely-positioned block requires us to reflow
                // its kids.
                if self.contains(REFLOW_OUT_OF_FLOW) {
                    self | REFLOW
                } else {
                    self
                }
            }
            _ => {
                // TODO(pcwalton): Take floatedness into account.
                self & (REPAINT | REFLOW)
            }
        }
    }
}

impl fmt::Display for RestyleDamage {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        let mut first_elem = true;

        let to_iter =
            [ (REPAINT, "Repaint")
            , (STORE_OVERFLOW, "StoreOverflow")
            , (BUBBLE_ISIZES, "BubbleISizes")
            , (REFLOW_OUT_OF_FLOW, "ReflowOutOfFlow")
            , (REFLOW, "Reflow")
            , (RESOLVE_GENERATED_CONTENT, "ResolveGeneratedContent")
            , (RECONSTRUCT_FLOW, "ReconstructFlow")
            ];

        for &(damage, damage_str) in &to_iter {
            if self.contains(damage) {
                if !first_elem { try!(write!(f, " | ")); }
                try!(write!(f, "{}", damage_str));
                first_elem = false;
            }
        }

        if first_elem {
            try!(write!(f, "NoDamage"));
        }

        Ok(())
    }
}

// NB: We need the braces inside the RHS due to Rust #8012.  This particular
// version of this macro might be safe anyway, but we want to avoid silent
// breakage on modifications.
macro_rules! add_if_not_equal(
    ($old:ident, $new:ident, $damage:ident,
     [ $($effect:ident),* ], [ $($style_struct_getter:ident.$name:ident),* ]) => ({
        if $( ($old.$style_struct_getter().$name != $new.$style_struct_getter().$name) )||* {
            $damage.insert($($effect)|*);
        }
    })
);

pub fn compute_damage(old: Option<&Arc<ComputedValues>>, new: &ComputedValues) -> RestyleDamage {
    let old: &ComputedValues = match old {
        None => return RestyleDamage::rebuild_and_reflow(),
        Some(cv) => &**cv,
    };

    let mut damage = RestyleDamage::empty();

    // This checks every CSS property, as enumerated in
    // impl<'self> CssComputedStyle<'self>
    // in src/support/netsurfcss/rust-netsurfcss/netsurfcss.rc.

    // FIXME: We can short-circuit more of this.

    add_if_not_equal!(old, new, damage,
                      [ REPAINT ], [
        get_color.color, get_background.background_color,
        get_border.border_top_color, get_border.border_right_color,
        get_border.border_bottom_color, get_border.border_left_color,
        get_effects.transform, get_box.z_index
    ]);

    add_if_not_equal!(old, new, damage,
                      [ REPAINT, STORE_OVERFLOW, REFLOW_OUT_OF_FLOW ], [
        get_positionoffsets.top, get_positionoffsets.left,
        get_positionoffsets.right, get_positionoffsets.bottom
    ]);

    add_if_not_equal!(old, new, damage,
                      [ REPAINT, STORE_OVERFLOW, BUBBLE_ISIZES, REFLOW_OUT_OF_FLOW, REFLOW ], [
        get_border.border_top_width, get_border.border_right_width,
        get_border.border_bottom_width, get_border.border_left_width,
        get_margin.margin_top, get_margin.margin_right,
        get_margin.margin_bottom, get_margin.margin_left,
        get_padding.padding_top, get_padding.padding_right,
        get_padding.padding_bottom, get_padding.padding_left,
        get_box.width, get_box.height,
        get_inheritedtext.text_align, get_text.text_decoration, get_inheritedbox.line_height
    ]);

    add_if_not_equal!(old, new, damage,
                      [
                        REPAINT,
                        STORE_OVERFLOW,
                        BUBBLE_ISIZES,
                        REFLOW_OUT_OF_FLOW,
                        REFLOW,
                        RECONSTRUCT_FLOW
                      ], [
        get_box.float, get_box.display, get_box.position, get_box.content,
        get_counters.counter_reset, get_counters.counter_increment,
        get_list.quotes, get_list.list_style_type,

        // If these text or font properties change, we need to reconstruct the flow so that
        // text shaping is re-run.
        get_inheritedtext.letter_spacing, get_inheritedtext.text_rendering,
        get_inheritedtext.text_transform, get_inheritedtext.word_spacing,
        get_font.font_family, get_font.font_style, get_font.font_variant, get_font.font_weight,
        get_font.font_size, get_font.font_stretch
    ]);

    // If the layer requirements of this flow have changed due to the value
    // of the transform, then reflow is required to rebuild the layers.
    if old.transform_requires_layer() != new.transform_requires_layer() {
        damage.insert(RestyleDamage::rebuild_and_reflow());
    }

    // FIXME: test somehow that we checked every CSS property
    damage
}

pub trait LayoutDamageComputation {
    fn compute_layout_damage(self) -> SpecialRestyleDamage;
    fn reflow_entire_document(self);
}

impl<'a> LayoutDamageComputation for &'a mut Flow {
    fn compute_layout_damage(self) -> SpecialRestyleDamage {
        let mut special_damage = SpecialRestyleDamage::empty();
        let is_absolutely_positioned = flow::base(self).flags.contains(IS_ABSOLUTELY_POSITIONED);

        // In addition to damage, we use this phase to compute whether nodes affect CSS counters.
        let mut has_counter_affecting_children = false;

        {
            let self_base = flow::mut_base(self);
            for kid in self_base.children.iter_mut() {
                let child_is_absolutely_positioned =
                    flow::base(kid).flags.contains(IS_ABSOLUTELY_POSITIONED);
                flow::mut_base(kid).restyle_damage
                                   .insert(self_base.restyle_damage.damage_for_child(
                                            is_absolutely_positioned,
                                            child_is_absolutely_positioned));
                {
                    let kid: &mut Flow = kid;
                    special_damage.insert(kid.compute_layout_damage());
                }
                self_base.restyle_damage
                         .insert(flow::base(kid).restyle_damage.damage_for_parent(
                                 child_is_absolutely_positioned));

                has_counter_affecting_children = has_counter_affecting_children ||
                    flow::base(kid).flags.intersects(AFFECTS_COUNTERS |
                                                     HAS_COUNTER_AFFECTING_CHILDREN);

            }
        }

        let self_base = flow::mut_base(self);
        if self_base.flags.float_kind() != float::T::none &&
                self_base.restyle_damage.intersects(REFLOW) {
            special_damage.insert(REFLOW_ENTIRE_DOCUMENT);
        }

        if has_counter_affecting_children {
            self_base.flags.insert(HAS_COUNTER_AFFECTING_CHILDREN)
        } else {
            self_base.flags.remove(HAS_COUNTER_AFFECTING_CHILDREN)
        }

        special_damage
    }

    fn reflow_entire_document(self) {
        let self_base = flow::mut_base(self);
        self_base.restyle_damage.insert(RestyleDamage::rebuild_and_reflow());
        self_base.restyle_damage.remove(RECONSTRUCT_FLOW);
        for kid in self_base.children.iter_mut() {
            kid.reflow_entire_document();
        }
    }
}
<commit_msg>layout: Minor whitespace cleanup.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use flow::{self, AFFECTS_COUNTERS, Flow, HAS_COUNTER_AFFECTING_CHILDREN, IS_ABSOLUTELY_POSITIONED};
use std::fmt;
use std::sync::Arc;
use style::computed_values::float;
use style::dom::TRestyleDamage;
use style::properties::ComputedValues;

bitflags! {
    #[doc = "Individual layout actions that may be necessary after restyling."]
    flags RestyleDamage: u8 {
        #[doc = "Repaint the node itself."]
        #[doc = "Currently unused; need to decide how this propagates."]
        const REPAINT = 0x01,

        #[doc = "Recompute the overflow regions (bounding box of object and all descendants)."]
        #[doc = "Propagates down the flow tree because the computation is bottom-up."]
        const STORE_OVERFLOW = 0x02,

        #[doc = "Recompute intrinsic inline_sizes (minimum and preferred)."]
        #[doc = "Propagates down the flow tree because the computation is"]
        #[doc = "bottom-up."]
        const BUBBLE_ISIZES = 0x04,

        #[doc = "Recompute actual inline-sizes and block-sizes, only taking out-of-flow children \
                 into account. \
                 Propagates up the flow tree because the computation is top-down."]
        const REFLOW_OUT_OF_FLOW = 0x08,

        #[doc = "Recompute actual inline_sizes and block_sizes."]
        #[doc = "Propagates up the flow tree because the computation is"]
        #[doc = "top-down."]
        const REFLOW = 0x10,

        #[doc = "Re-resolve generated content. \
                 Propagates up the flow tree because the computation is inorder."]
        const RESOLVE_GENERATED_CONTENT = 0x20,

        #[doc = "The entire flow needs to be reconstructed."]
        const RECONSTRUCT_FLOW = 0x40
    }
}

bitflags! {
    flags SpecialRestyleDamage: u8 {
        #[doc = "If this flag is set, we need to reflow the entire document. This is more or less a \
                 temporary hack to deal with cases that we don't handle incrementally yet."]
        const REFLOW_ENTIRE_DOCUMENT = 0x01,
    }
}

impl TRestyleDamage for RestyleDamage {
    fn compute(old: Option<&Arc<ComputedValues>>, new: &ComputedValues) -> RestyleDamage { compute_damage(old, new) }

    /// Returns a bitmask that represents a flow that needs to be rebuilt and reflowed.
    ///
    /// Use this instead of `RestyleDamage::all()` because `RestyleDamage::all()` will result in
    /// unnecessary sequential resolution of generated content.
    fn rebuild_and_reflow() -> RestyleDamage {
        REPAINT | STORE_OVERFLOW | BUBBLE_ISIZES | REFLOW_OUT_OF_FLOW | REFLOW | RECONSTRUCT_FLOW
    }
}

impl RestyleDamage {
    /// Supposing a flow has the given `position` property and this damage, returns the damage that
    /// we should add to the *parent* of this flow.
    pub fn damage_for_parent(self, child_is_absolutely_positioned: bool) -> RestyleDamage {
        if child_is_absolutely_positioned {
            self & (REPAINT | STORE_OVERFLOW | REFLOW_OUT_OF_FLOW | RESOLVE_GENERATED_CONTENT)
        } else {
            self & (REPAINT | STORE_OVERFLOW | REFLOW | REFLOW_OUT_OF_FLOW |
                    RESOLVE_GENERATED_CONTENT)
        }
    }

    /// Supposing the *parent* of a flow with the given `position` property has this damage,
    /// returns the damage that we should add to this flow.
    pub fn damage_for_child(self,
                            parent_is_absolutely_positioned: bool,
                            child_is_absolutely_positioned: bool)
                            -> RestyleDamage {
        match (parent_is_absolutely_positioned, child_is_absolutely_positioned) {
            (false, true) => {
                // Absolute children are out-of-flow and therefore insulated from changes.
                //
                // FIXME(pcwalton): Au contraire, if the containing block dimensions change!
                self & REPAINT
            }
            (true, false) => {
                // Changing the position of an absolutely-positioned block requires us to reflow
                // its kids.
                if self.contains(REFLOW_OUT_OF_FLOW) {
                    self | REFLOW
                } else {
                    self
                }
            }
            _ => {
                // TODO(pcwalton): Take floatedness into account.
                self & (REPAINT | REFLOW)
            }
        }
    }
}

impl fmt::Display for RestyleDamage {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        let mut first_elem = true;

        let to_iter =
            [ (REPAINT, "Repaint")
            , (STORE_OVERFLOW, "StoreOverflow")
            , (BUBBLE_ISIZES, "BubbleISizes")
            , (REFLOW_OUT_OF_FLOW, "ReflowOutOfFlow")
            , (REFLOW, "Reflow")
            , (RESOLVE_GENERATED_CONTENT, "ResolveGeneratedContent")
            , (RECONSTRUCT_FLOW, "ReconstructFlow")
            ];

        for &(damage, damage_str) in &to_iter {
            if self.contains(damage) {
                if !first_elem { try!(write!(f, " | ")); }
                try!(write!(f, "{}", damage_str));
                first_elem = false;
            }
        }

        if first_elem {
            try!(write!(f, "NoDamage"));
        }

        Ok(())
    }
}

// NB: We need the braces inside the RHS due to Rust #8012.  This particular
// version of this macro might be safe anyway, but we want to avoid silent
// breakage on modifications.
macro_rules! add_if_not_equal(
    ($old:ident, $new:ident, $damage:ident,
     [ $($effect:ident),* ], [ $($style_struct_getter:ident.$name:ident),* ]) => ({
        if $( ($old.$style_struct_getter().$name != $new.$style_struct_getter().$name) )||* {
            $damage.insert($($effect)|*);
        }
    })
);

pub fn compute_damage(old: Option<&Arc<ComputedValues>>, new: &ComputedValues) -> RestyleDamage {
    let old: &ComputedValues = match old {
        None => return RestyleDamage::rebuild_and_reflow(),
        Some(cv) => &**cv,
    };

    let mut damage = RestyleDamage::empty();

    // This checks every CSS property, as enumerated in
    // impl<'self> CssComputedStyle<'self>
    // in src/support/netsurfcss/rust-netsurfcss/netsurfcss.rc.

    // FIXME: We can short-circuit more of this.

    add_if_not_equal!(old, new, damage,
                      [ REPAINT ], [
        get_color.color, get_background.background_color,
        get_border.border_top_color, get_border.border_right_color,
        get_border.border_bottom_color, get_border.border_left_color,
        get_effects.transform, get_box.z_index
    ]);

    add_if_not_equal!(old, new, damage,
                      [ REPAINT, STORE_OVERFLOW, REFLOW_OUT_OF_FLOW ], [
        get_positionoffsets.top, get_positionoffsets.left,
        get_positionoffsets.right, get_positionoffsets.bottom
    ]);

    add_if_not_equal!(old, new, damage,
                      [ REPAINT, STORE_OVERFLOW, BUBBLE_ISIZES, REFLOW_OUT_OF_FLOW, REFLOW ], [
        get_border.border_top_width, get_border.border_right_width,
        get_border.border_bottom_width, get_border.border_left_width,
        get_margin.margin_top, get_margin.margin_right,
        get_margin.margin_bottom, get_margin.margin_left,
        get_padding.padding_top, get_padding.padding_right,
        get_padding.padding_bottom, get_padding.padding_left,
        get_box.width, get_box.height,
        get_inheritedtext.text_align, get_text.text_decoration, get_inheritedbox.line_height
    ]);

    add_if_not_equal!(old, new, damage,
                      [
                        REPAINT,
                        STORE_OVERFLOW,
                        BUBBLE_ISIZES,
                        REFLOW_OUT_OF_FLOW,
                        REFLOW,
                        RECONSTRUCT_FLOW
                      ], [
        get_box.float, get_box.display, get_box.position, get_box.content,
        get_counters.counter_reset, get_counters.counter_increment,
        get_list.quotes, get_list.list_style_type,

        // If these text or font properties change, we need to reconstruct the flow so that
        // text shaping is re-run.
        get_inheritedtext.letter_spacing, get_inheritedtext.text_rendering,
        get_inheritedtext.text_transform, get_inheritedtext.word_spacing,
        get_font.font_family, get_font.font_style, get_font.font_variant, get_font.font_weight,
        get_font.font_size, get_font.font_stretch
    ]);

    // If the layer requirements of this flow have changed due to the value
    // of the transform, then reflow is required to rebuild the layers.
    if old.transform_requires_layer() != new.transform_requires_layer() {
        damage.insert(RestyleDamage::rebuild_and_reflow());
    }

    // FIXME: test somehow that we checked every CSS property
    damage
}

pub trait LayoutDamageComputation {
    fn compute_layout_damage(self) -> SpecialRestyleDamage;
    fn reflow_entire_document(self);
}

impl<'a> LayoutDamageComputation for &'a mut Flow {
    fn compute_layout_damage(self) -> SpecialRestyleDamage {
        let mut special_damage = SpecialRestyleDamage::empty();
        let is_absolutely_positioned = flow::base(self).flags.contains(IS_ABSOLUTELY_POSITIONED);

        // In addition to damage, we use this phase to compute whether nodes affect CSS counters.
        let mut has_counter_affecting_children = false;

        {
            let self_base = flow::mut_base(self);
            for kid in self_base.children.iter_mut() {
                let child_is_absolutely_positioned =
                    flow::base(kid).flags.contains(IS_ABSOLUTELY_POSITIONED);
                flow::mut_base(kid).restyle_damage
                                   .insert(self_base.restyle_damage.damage_for_child(
                                            is_absolutely_positioned,
                                            child_is_absolutely_positioned));
                {
                    let kid: &mut Flow = kid;
                    special_damage.insert(kid.compute_layout_damage());
                }
                self_base.restyle_damage
                         .insert(flow::base(kid).restyle_damage.damage_for_parent(
                                 child_is_absolutely_positioned));

                has_counter_affecting_children = has_counter_affecting_children ||
                    flow::base(kid).flags.intersects(AFFECTS_COUNTERS |
                                                     HAS_COUNTER_AFFECTING_CHILDREN);
            }
        }

        let self_base = flow::mut_base(self);
        if self_base.flags.float_kind() != float::T::none &&
                self_base.restyle_damage.intersects(REFLOW) {
            special_damage.insert(REFLOW_ENTIRE_DOCUMENT);
        }

        if has_counter_affecting_children {
            self_base.flags.insert(HAS_COUNTER_AFFECTING_CHILDREN)
        } else {
            self_base.flags.remove(HAS_COUNTER_AFFECTING_CHILDREN)
        }

        special_damage
    }

    fn reflow_entire_document(self) {
        let self_base = flow::mut_base(self);
        self_base.restyle_damage.insert(RestyleDamage::rebuild_and_reflow());
        self_base.restyle_damage.remove(RECONSTRUCT_FLOW);
        for kid in self_base.children.iter_mut() {
            kid.reflow_entire_document();
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 Pierre Talbot (IRCAM)

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

//     http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! A variable is a pair `(location, value)` where the value lives inside a block of memory, called the *variables store*. The store is parametrized by the type of the values it will contain, it is the *domain* of the variables.
//!
//! We initialize a variable in the store by providing the associated value and the store gives back a location associated to it. Further read or write operations of the value must be done explicitly through the store with the location returned during the allocation. An important property is that only monotonic updates of a value are allowed, it means that the domains can only become smaller. For example, the variable `x: [1..10]` can be updated to `x: [2..10]` or `x: [5..5]` but not to `x: [1..11]`.
//!
//! A subset of arithmetics is provided with *views* on variables. It allows to manipulate an expression such as `x + 5` as if it was a single variable and to avoid implementing specific instances of propagation algorithms such as `x + c < y` which is just `x < y` with `x` being a view. The view acts as a proxy between operations on variable and the store. It implies that operations must be called on the views instead of applying them directly to the store.

pub mod memory;
pub mod concept;
pub mod ops;
pub mod store;

pub use variable::ops::Iterable;

use variable::store::*;
use variable::memory::CopyMemory;
use propagation::events::FDEvent;
use interval::interval::*;

pub type VStoreCopy<Domain> = Store<CopyMemory<Domain>, FDEvent>;
pub type VStoreFD = VStoreCopy<Interval<i32>>;
<commit_msg>[Restoration][Variable] Default to timestamp trail memory.<commit_after>// Copyright 2015 Pierre Talbot (IRCAM)

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

//     http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! A variable is a pair `(location, value)` where the value lives inside a block of memory, called the *variables store*. The store is parametrized by the type of the values it will contain, it is the *domain* of the variables.
//!
//! We initialize a variable in the store by providing the associated value and the store gives back a location associated to it. Further read or write operations of the value must be done explicitly through the store with the location returned during the allocation. An important property is that only monotonic updates of a value are allowed, it means that the domains can only become smaller. For example, the variable `x: [1..10]` can be updated to `x: [2..10]` or `x: [5..5]` but not to `x: [1..11]`.
//!
//! A subset of arithmetics is provided with *views* on variables. It allows to manipulate an expression such as `x + 5` as if it was a single variable and to avoid implementing specific instances of propagation algorithms such as `x + c < y` which is just `x < y` with `x` being a view. The view acts as a proxy between operations on variable and the store. It implies that operations must be called on the views instead of applying them directly to the store.

pub mod memory;
pub mod concept;
pub mod ops;
pub mod store;

pub use variable::ops::Iterable;

use variable::store::*;
use variable::memory::TimestampTrailMemory;
use propagation::events::FDEvent;
use interval::interval::*;

pub type VStoreCopy<Domain> = Store<TimestampTrailMemory<Domain>, FDEvent>;
pub type VStoreFD = VStoreCopy<Interval<i32>>;
<|endoftext|>


<commit_before>use core::ptr;

pub struct UnsafeList<T> {
    head: Link<Node<T>>,
}

impl<T> UnsafeList<T> {
    pub unsafe fn new() -> Self {
        UnsafeList {
            head: Link::none(),
        }
    }

    pub unsafe fn push_front(&mut self, mut elem: Link<Node<T>>) {
        match self.head.as_mut() {
            None => {
                if let Some(ref mut node) = elem.as_mut() {
                    node.prev = Link::none();
                    node.next = Link::none();
                }
            },
            Some(ref mut node) => {
                if let Some(ref mut elem_node) = elem.as_mut() {
                    node.prev = elem.clone();

                    elem_node.next = self.head.clone();
                    elem_node.prev = Link::none();
                }
            }
        }

        self.head = elem;
    }

    pub unsafe fn pop(&mut self, mut elem: Link<Node<T>>) {
        if let Some(node) = elem.as_mut() {
            if let Some(prev) = node.prev.as_mut() {
                prev.next = node.next.clone();
            }

            if let Some(next) = node.next.as_mut() {
                next.prev = node.prev.clone();
            }
        } else {
            return;
        }

        if elem == self.head {
            if let Some(head) = self.head.as_mut() {
                self.head = head.next.clone();
            } else {
                self.head = Link::none();
            }
        }
    }
}

pub struct Node<T> {
    pub elem: T,
    pub prev: Link<Node<T>>,
    pub next: Link<Node<T>>,
}

impl<T> Node<T> {
    pub fn new(elem: T) -> Self {
        Node {
            elem: elem,
            prev: Link::none(),
            next: Link::none(),
        }
    }
}

pub struct Link<T> {
    ptr: *mut T,
}

impl<T> Link<T> {
    pub fn some(ptr: *mut T) -> Self {
        Link {
            ptr: ptr,
        }
    }

    pub fn none() -> Self {
        Link {
            ptr: ptr::null_mut(),
        }
    }

    pub fn is_null(&self) -> bool {
        self.ptr == ptr::null_mut()
    }

    pub unsafe fn as_ref<'a>(&self) -> Option<&'a T> {
        self.ptr.as_ref()
    }

    pub unsafe fn as_mut<'a>(&mut self) -> Option<&'a mut T> {
        self.ptr.as_mut()
    }
}

impl<T> Clone for Link<T> {
    fn clone(&self) -> Self {
        Link {
            ptr: self.ptr,
        }
    }
}

impl<T> PartialEq<Link<T>> for Link<T> {
    fn eq(&self, rhs: &Link<T>) -> bool {
        self.ptr == rhs.ptr
    }
}
<commit_msg>heap: Add cursor method to UnsafeList<T><commit_after>use core::ptr;

pub struct UnsafeList<T> {
    head: Link<Node<T>>,
}

impl<T> UnsafeList<T> {
    pub unsafe fn new() -> Self {
        UnsafeList {
            head: Link::none(),
        }
    }

    pub unsafe fn push_front(&mut self, mut elem: Link<Node<T>>) {
        match self.head.as_mut() {
            None => {
                if let Some(ref mut node) = elem.as_mut() {
                    node.prev = Link::none();
                    node.next = Link::none();
                }
            },
            Some(ref mut node) => {
                if let Some(ref mut elem_node) = elem.as_mut() {
                    node.prev = elem.clone();

                    elem_node.next = self.head.clone();
                    elem_node.prev = Link::none();
                }
            }
        }

        self.head = elem;
    }

    pub unsafe fn pop(&mut self, mut elem: Link<Node<T>>) {
        if let Some(node) = elem.as_mut() {
            if let Some(prev) = node.prev.as_mut() {
                prev.next = node.next.clone();
            }

            if let Some(next) = node.next.as_mut() {
                next.prev = node.prev.clone();
            }
        } else {
            return;
        }

        if elem == self.head {
            if let Some(head) = self.head.as_mut() {
                self.head = head.next.clone();
            } else {
                self.head = Link::none();
            }
        }
    }

    pub unsafe fn cursor<'a>(&'a mut self) -> UnsafeCursor<'a, T> {
        let head_dup = self.head.clone();

        UnsafeCursor {
            head: &mut self.head,
            current: head_dup,
        }
    }
}

pub struct UnsafeCursor<'a, T: 'a> {
    head: &'a mut Link<Node<T>>,
    current: Link<Node<T>>,
}

impl<'a, T: 'a> UnsafeCursor<'a, T> {
    pub unsafe fn remove(&mut self) -> Link<Node<T>> {
        if self.current.is_null() {
            return Link::none();
        }

        let mut res = self.current.clone();
        let update_head = *self.head == self.current;

        self.next();

        if let Some(node) = res.as_mut() {
            if let Some(prev) = node.prev.as_mut() {
                prev.next = node.next.clone();
            }

            if let Some(next) = node.next.as_mut() {
                next.prev = node.prev.clone();
            }

            node.next = Link::none();
            node.prev = Link::none();
        }

        if update_head {
            *self.head = self.current.clone();
        }

        res
    }

    pub unsafe fn next(&mut self) {
        if let Some(node) = self.current.as_mut() {
            self.current = node.next.clone();
        }
    }

    pub unsafe fn as_ref(&self) -> Option<&Node<T>> {
        self.current.as_ref()
    }
}

pub struct Node<T> {
    pub elem: T,
    pub prev: Link<Node<T>>,
    pub next: Link<Node<T>>,
}

impl<T> Node<T> {
    pub fn new(elem: T) -> Self {
        Node {
            elem: elem,
            prev: Link::none(),
            next: Link::none(),
        }
    }
}

pub struct Link<T> {
    ptr: *mut T,
}

impl<T> Link<T> {
    pub fn some(ptr: *mut T) -> Self {
        Link {
            ptr: ptr,
        }
    }

    pub fn none() -> Self {
        Link {
            ptr: ptr::null_mut(),
        }
    }

    pub fn is_null(&self) -> bool {
        self.ptr == ptr::null_mut()
    }

    pub unsafe fn as_ref<'a>(&self) -> Option<&'a T> {
        self.ptr.as_ref()
    }

    pub unsafe fn as_mut<'a>(&mut self) -> Option<&'a mut T> {
        self.ptr.as_mut()
    }
}

impl<T> Clone for Link<T> {
    fn clone(&self) -> Self {
        Link {
            ptr: self.ptr,
        }
    }
}

impl<T> PartialEq<Link<T>> for Link<T> {
    fn eq(&self, rhs: &Link<T>) -> bool {
        self.ptr == rhs.ptr
    }
}
<|endoftext|>


<commit_before>use std::ffi::{CStr, CString};
use std::marker::PhantomData;

use tensorflow_sys as tf;

use crate::eager::Context;
use crate::{AnyTensor, DataType, Result, Status, Tensor, TensorType};

/// A handle to a tensor on a device.
///
/// Constructing a TensorHandle requires a reference to an execute context so that the
/// generated handle will not out live the context.
/// ```
/// # use tensorflow::Tensor;
/// use tensorflow::eager::{ContextOptions, Context, TensorHandle};
///
/// let opts = ContextOptions::new();
/// let ctx = Context::new(opts).unwrap();
///
/// let t = Tensor::from(&[3i32]);
/// let h = TensorHandle::new(&ctx, &t).unwrap();
/// let v: Tensor<i32> = h.resolve().unwrap();
/// assert_eq!(&v[..], &[3i32]);
/// ```
///
/// Since TensorHandle cannot be alive beyond the lifetime of the context, the following
/// code will not compile.
/// ```compile_fail
/// # use tensorflow::Tensor;
/// use tensorflow::eager::{ContextOptions, Context, TensorHandle};
///
/// let h = {
///     let opts = ContextOptions::new();
///     let ctx = Context::new(opts).unwrap();
///
///     let t = Tensor::from(&[3i32]);
///     let h = TensorHandle::new(&ctx, &t).unwrap();
///     h
/// };
/// ```
#[derive(Debug)]
pub struct TensorHandle<'a> {
    inner: *mut tf::TFE_TensorHandle,
    // TensorHandle should not live longer than a given context.
    ctx: PhantomData<&'a Context>,
}

impl<'a> Drop for TensorHandle<'a> {
    fn drop(&mut self) {
        unsafe {
            tf::TFE_DeleteTensorHandle(self.inner);
        }
    }
}

impl<'a> TensorHandle<'a> {
    /// Crate a TensorHandle from the input Tensor
    pub fn new<T: TensorType>(_ctx: &'a Context, t: &Tensor<T>) -> Result<TensorHandle<'a>> {
        let status = Status::new();
        let inner = unsafe { tf::TFE_NewTensorHandle(t.inner()?, status.inner) };

        if inner.is_null() {
            Err(status)
        } else {
            Ok(TensorHandle {
                inner,
                ctx: PhantomData,
            })
        }
    }

    /// Return the DataType that corresponds to this type.
    pub fn data_type(&self) -> DataType {
        unsafe { DataType::from_c(tf::TFE_TensorHandleDataType(self.inner)) }
    }

    /// Return the number of dimensions.
    ///
    /// This function will block till the operation that produces the TensorHandle has completed.
    pub fn num_dims(&self) -> Result<usize> {
        let status = Status::new();
        let num_dims = unsafe { tf::TFE_TensorHandleNumDims(self.inner, status.inner) };
        if status.is_ok() {
            // num_dims >= 0 when the status is ok, so we can safely cast it to u64.
            Ok(num_dims as usize)
        } else {
            Err(status)
        }
    }

    /// Return the number of elements
    pub fn num_elements(&self) -> Result<u64> {
        let status = Status::new();
        let num_elements = unsafe { tf::TFE_TensorHandleNumElements(self.inner, status.inner) };
        if status.is_ok() {
            // num_elements >= 0 when the status is ok, so we can safely cast it to u64.
            Ok(num_elements as u64)
        } else {
            Err(status)
        }
    }

    /// Return the number of elements for a given dim_index.
    ///
    /// This function will block till the operation that produces the TensorHandle has completed.
    pub fn dim(&self, dim_index: i32) -> Result<u64> {
        let status = Status::new();
        let dim = unsafe { tf::TFE_TensorHandleDim(self.inner, dim_index, status.inner) };
        if status.is_ok() {
            // dim >= 0 when the status is ok, so we can safely cast it to u64.
            Ok(dim as u64)
        } else {
            Err(status)
        }
    }

    /// Return the device of the operation that produced the current TensorHandle.
    ///
    /// If the TensorHandle was produced by a copy, returns the destination device of the copy.
    /// Note that the returned device name is not always the device holding the tensor handle's memory.
    /// If you want the latter, use backing_device_name.
    ///
    /// This function will block till the operation that produces the current TensorHandle has completed.
    pub fn device_name(&self) -> Result<String> {
        let status = Status::new();
        unsafe {
            let device_name = tf::TFE_TensorHandleDeviceName(self.inner, status.inner);
            if status.is_ok() {
                Ok(CStr::from_ptr(device_name).to_str()?.to_string())
            } else {
                Err(status)
            }
        }
    }

    /// Returns the name of the device in whose memory underlying the current TensorHandle resides.
    ///
    /// This function will block till the operation that produces the current TensorHandle has completed.
    pub fn backing_device_name(&self) -> Result<String> {
        let status = Status::new();
        unsafe {
            let device_name = tf::TFE_TensorHandleBackingDeviceName(self.inner, status.inner);
            if status.is_ok() {
                Ok(CStr::from_ptr(device_name).to_str()?.to_string())
            } else {
                Err(status)
            }
        }
    }

    /// Return a new TensorHandle that shares the underlying tensor with the current TensorHandle.
    pub fn copy_sharing_tensor(&self) -> Result<Self> {
        let status = Status::new();
        let inner = unsafe { tf::TFE_TensorHandleCopySharingTensor(self.inner, status.inner) };
        if status.is_ok() {
            Ok(Self {
                inner,
                ctx: self.ctx,
            })
        } else {
            Err(status)
        }
    }

    /// This function will block till the operation that produces the current TensorHandle has completed.
    /// The memory returned might alias the internal memory used by TensorFlow.
    /// Hence, callers should not mutate this memory.
    pub fn resolve<T: TensorType>(&self) -> Result<Tensor<T>> {
        let mut status = Status::new();
        let tf_tensor = unsafe { tf::TFE_TensorHandleResolve(self.inner, status.inner) };
        if !status.is_ok() {
            return Err(status);
        }
        if self.data_type() != T::data_type() {
            let msg = format!(
                "The expected data type ({}) and underlying data type ({}) did not match.",
                T::data_type(),
                self.data_type()
            );

            status.set_lossy(crate::Code::InvalidArgument, &msg);
            return Err(status);
        }

        // Safely unwrap since data_type was checked beforehand.
        unsafe { Ok(Tensor::from_tf_tensor(tf_tensor).unwrap()) }
    }

    /// Create a new TensorHandle with the same contents as `h` but placed
    /// in the memory of the device name 'device_name'.
    /// If source and destination are the same device, then this creates a new handle
    /// that shares the underlying buffer. Otherwise, it currently requires at least
    /// one of the source or destination devices to be CPU (i.e., for the source or
    /// destination tensor to be placed in host memory).
    /// If async execution is enabled, the copy may be enqueued and the call will
    /// return "non-ready" TensorHandle. Else, this function returns after the copy has
    /// been done.
    pub fn copy_to_device(h: &TensorHandle, ctx: &'a Context, device_name: &str) -> Result<Self> {
        let status = Status::new();

        let device_name = CString::new(device_name)?;
        unsafe {
            let inner = tf::TFE_TensorHandleCopyToDevice(
                h.inner,
                ctx.inner,
                device_name.as_ptr(),
                status.inner,
            );

            if status.is_ok() {
                Ok(Self {
                    inner,
                    ctx: PhantomData,
                })
            } else {
                Err(status)
            }
        }
    }
}

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

    #[test]
    fn test_tensor_handle() {
        let opts = ContextOptions::new();
        let ctx = Context::new(opts).unwrap();

        let t = Tensor::new(&[2, 3])
            .with_values(&[0_i32, 1, 2, 3, 4, 5])
            .unwrap();
        let h = TensorHandle::new(&ctx, &t).unwrap();

        assert_eq!(h.data_type(), DataType::Int32);
        assert_eq!(h.num_elements().unwrap(), 6);
        assert_eq!(h.num_dims().unwrap(), 2);
        assert_eq!(h.dim(0).unwrap(), 2);
        assert_eq!(h.dim(1).unwrap(), 3);
    }

    #[test]
    fn test_copy_sharing_tensor() {
        let opts = ContextOptions::new();
        let ctx = Context::new(opts).unwrap();

        let t = Tensor::new(&[2, 3])
            .with_values(&[0_i32, 1, 2, 3, 4, 5])
            .unwrap();
        let h = TensorHandle::new(&ctx, &t).unwrap();
        let h_copy = h.copy_sharing_tensor().unwrap();
        let t2 = h_copy.resolve::<i32>().unwrap();

        // t and t2 may share the same memory, but it's difficuly to check
        // since the `resolve` does not guarantee that.
        assert_eq!(&t[..], &t2[..]);
    }

    #[cfg(feature = "tensorflow_gpu")]
    #[test]
    fn test_copy_to_device() {
        let opts = ContextOptions::new();
        let ctx = Context::new(opts).unwrap();
        let devices = ctx.device_list().unwrap();
        if !(devices.iter().any(|d| d.device_type == "GPU")) {
            eprintln!("Skipping test_copy_to_device because no GPU device is found");
            return;
        }

        let t = Tensor::new(&[2, 2]).with_values(&[0_i32, 1, 2, 3]).unwrap();
        let h = TensorHandle::new(&ctx, &t).unwrap();
        let target_device = "/job:localhost/replica:0/task:0/device:GPU:0";
        let h_gpu = TensorHandle::copy_to_device(&h, &ctx, target_device).unwrap();
        assert_eq!(h_gpu.device_name().unwrap(), target_device);
        let t2 = h_gpu.resolve::<i32>().unwrap();

        assert_eq!(&t[..], &t2[..]);
    }

    #[cfg(feature = "tensorflow_gpu")]
    #[test]
    fn test_copy_to_device_lifetime() {
        let values = [0_i32, 1, 2, 3];
        let target_device = "/job:localhost/replica:0/task:0/device:GPU:0";

        let opts = ContextOptions::new();
        let ctx = Context::new(opts).unwrap();
        let devices = ctx.device_list().unwrap();
        if !(devices.iter().any(|d| d.device_type == "GPU")) {
            eprintln!("Skipping test_copy_to_device because no GPU device is found");
            return;
        }

        let h_gpu = {
            // Create a temporal Context
            let opts = ContextOptions::new();
            let ctx2 = Context::new(opts).unwrap();
            let t = Tensor::new(&[2, 2]).with_values(&values).unwrap();

            // Create a TensorHandle managed by the context `ctx2`.
            let h = TensorHandle::new(&ctx2, &t).unwrap();

            // Copy to GPU. This creates a new handle managed by the context `ctx`.
            TensorHandle::copy_to_device(&h, &ctx, target_device).unwrap()
        };
        assert_eq!(h_gpu.device_name().unwrap(), target_device);
        let t2 = h_gpu.resolve::<i32>().unwrap();

        assert_eq!(&values[..], &t2[..]);
    }
}
<commit_msg>Modify tthe copy_to_device's lifetime handling<commit_after>use std::ffi::{CStr, CString};
use std::marker::PhantomData;

use tensorflow_sys as tf;

use crate::eager::Context;
use crate::{AnyTensor, DataType, Result, Status, Tensor, TensorType};

/// A handle to a tensor on a device.
///
/// Constructing a TensorHandle requires a reference to an execute context so that the
/// generated handle will not out live the context.
/// ```
/// # use tensorflow::Tensor;
/// use tensorflow::eager::{ContextOptions, Context, TensorHandle};
///
/// let opts = ContextOptions::new();
/// let ctx = Context::new(opts).unwrap();
///
/// let t = Tensor::from(&[3i32]);
/// let h = TensorHandle::new(&ctx, &t).unwrap();
/// let v: Tensor<i32> = h.resolve().unwrap();
/// assert_eq!(&v[..], &[3i32]);
/// ```
///
/// Since TensorHandle cannot be alive beyond the lifetime of the context, the following
/// code will not compile.
/// ```compile_fail
/// # use tensorflow::Tensor;
/// use tensorflow::eager::{ContextOptions, Context, TensorHandle};
///
/// let h = {
///     let opts = ContextOptions::new();
///     let ctx = Context::new(opts).unwrap();
///
///     let t = Tensor::from(&[3i32]);
///     let h = TensorHandle::new(&ctx, &t).unwrap();
///     h
/// };
/// ```
#[derive(Debug)]
pub struct TensorHandle<'a> {
    inner: *mut tf::TFE_TensorHandle,
    // TensorHandle should not live longer than a given context.
    ctx: PhantomData<&'a Context>,
}

impl<'a> Drop for TensorHandle<'a> {
    fn drop(&mut self) {
        unsafe {
            tf::TFE_DeleteTensorHandle(self.inner);
        }
    }
}

impl<'a> TensorHandle<'a> {
    /// Crate a TensorHandle from the input Tensor
    pub fn new<T: TensorType>(_ctx: &'a Context, t: &Tensor<T>) -> Result<TensorHandle<'a>> {
        let status = Status::new();
        let inner = unsafe { tf::TFE_NewTensorHandle(t.inner()?, status.inner) };

        if inner.is_null() {
            Err(status)
        } else {
            Ok(TensorHandle {
                inner,
                ctx: PhantomData,
            })
        }
    }

    /// Return the DataType that corresponds to this type.
    pub fn data_type(&self) -> DataType {
        unsafe { DataType::from_c(tf::TFE_TensorHandleDataType(self.inner)) }
    }

    /// Return the number of dimensions.
    ///
    /// This function will block till the operation that produces the TensorHandle has completed.
    pub fn num_dims(&self) -> Result<usize> {
        let status = Status::new();
        let num_dims = unsafe { tf::TFE_TensorHandleNumDims(self.inner, status.inner) };
        if status.is_ok() {
            // num_dims >= 0 when the status is ok, so we can safely cast it to u64.
            Ok(num_dims as usize)
        } else {
            Err(status)
        }
    }

    /// Return the number of elements
    pub fn num_elements(&self) -> Result<u64> {
        let status = Status::new();
        let num_elements = unsafe { tf::TFE_TensorHandleNumElements(self.inner, status.inner) };
        if status.is_ok() {
            // num_elements >= 0 when the status is ok, so we can safely cast it to u64.
            Ok(num_elements as u64)
        } else {
            Err(status)
        }
    }

    /// Return the number of elements for a given dim_index.
    ///
    /// This function will block till the operation that produces the TensorHandle has completed.
    pub fn dim(&self, dim_index: i32) -> Result<u64> {
        let status = Status::new();
        let dim = unsafe { tf::TFE_TensorHandleDim(self.inner, dim_index, status.inner) };
        if status.is_ok() {
            // dim >= 0 when the status is ok, so we can safely cast it to u64.
            Ok(dim as u64)
        } else {
            Err(status)
        }
    }

    /// Return the device of the operation that produced the current TensorHandle.
    ///
    /// If the TensorHandle was produced by a copy, returns the destination device of the copy.
    /// Note that the returned device name is not always the device holding the tensor handle's memory.
    /// If you want the latter, use backing_device_name.
    ///
    /// This function will block till the operation that produces the current TensorHandle has completed.
    pub fn device_name(&self) -> Result<String> {
        let status = Status::new();
        unsafe {
            let device_name = tf::TFE_TensorHandleDeviceName(self.inner, status.inner);
            if status.is_ok() {
                Ok(CStr::from_ptr(device_name).to_str()?.to_string())
            } else {
                Err(status)
            }
        }
    }

    /// Returns the name of the device in whose memory underlying the current TensorHandle resides.
    ///
    /// This function will block till the operation that produces the current TensorHandle has completed.
    pub fn backing_device_name(&self) -> Result<String> {
        let status = Status::new();
        unsafe {
            let device_name = tf::TFE_TensorHandleBackingDeviceName(self.inner, status.inner);
            if status.is_ok() {
                Ok(CStr::from_ptr(device_name).to_str()?.to_string())
            } else {
                Err(status)
            }
        }
    }

    /// Return a new TensorHandle that shares the underlying tensor with the current TensorHandle.
    pub fn copy_sharing_tensor(&self) -> Result<Self> {
        let status = Status::new();
        let inner = unsafe { tf::TFE_TensorHandleCopySharingTensor(self.inner, status.inner) };
        if status.is_ok() {
            Ok(Self {
                inner,
                ctx: self.ctx,
            })
        } else {
            Err(status)
        }
    }

    /// This function will block till the operation that produces the current TensorHandle has completed.
    /// The memory returned might alias the internal memory used by TensorFlow.
    /// Hence, callers should not mutate this memory.
    pub fn resolve<T: TensorType>(&self) -> Result<Tensor<T>> {
        let mut status = Status::new();
        let tf_tensor = unsafe { tf::TFE_TensorHandleResolve(self.inner, status.inner) };
        if !status.is_ok() {
            return Err(status);
        }
        if self.data_type() != T::data_type() {
            let msg = format!(
                "The expected data type ({}) and underlying data type ({}) did not match.",
                T::data_type(),
                self.data_type()
            );

            status.set_lossy(crate::Code::InvalidArgument, &msg);
            return Err(status);
        }

        // Safely unwrap since data_type was checked beforehand.
        unsafe { Ok(Tensor::from_tf_tensor(tf_tensor).unwrap()) }
    }

    /// Create a new TensorHandle with the same contents as the current TensorHandle but placed
    /// in the memory of the device name 'device_name'.
    /// If source and destination are the same device, then this creates a new handle
    /// that shares the underlying buffer. Otherwise, it currently requires at least
    /// one of the source or destination devices to be CPU (i.e., for the source or
    /// destination tensor to be placed in host memory).
    /// If async execution is enabled, the copy may be enqueued and the call will
    /// return "non-ready" TensorHandle. Else, this function returns after the copy has
    /// been done.
    pub fn copy_to_device<'b>(
        &self,
        ctx: &'b Context,
        device_name: &str,
    ) -> Result<TensorHandle<'b>> {
        let status = Status::new();

        let device_name = CString::new(device_name)?;
        unsafe {
            let inner = tf::TFE_TensorHandleCopyToDevice(
                self.inner,
                ctx.inner,
                device_name.as_ptr(),
                status.inner,
            );

            if status.is_ok() {
                Ok(TensorHandle {
                    inner,
                    ctx: PhantomData,
                })
            } else {
                Err(status)
            }
        }
    }
}

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

    #[test]
    fn test_tensor_handle() {
        let opts = ContextOptions::new();
        let ctx = Context::new(opts).unwrap();

        let t = Tensor::new(&[2, 3])
            .with_values(&[0_i32, 1, 2, 3, 4, 5])
            .unwrap();
        let h = TensorHandle::new(&ctx, &t).unwrap();

        assert_eq!(h.data_type(), DataType::Int32);
        assert_eq!(h.num_elements().unwrap(), 6);
        assert_eq!(h.num_dims().unwrap(), 2);
        assert_eq!(h.dim(0).unwrap(), 2);
        assert_eq!(h.dim(1).unwrap(), 3);
    }

    #[test]
    fn test_copy_sharing_tensor() {
        let opts = ContextOptions::new();
        let ctx = Context::new(opts).unwrap();

        let t = Tensor::new(&[2, 3])
            .with_values(&[0_i32, 1, 2, 3, 4, 5])
            .unwrap();
        let h = TensorHandle::new(&ctx, &t).unwrap();
        let h_copy = h.copy_sharing_tensor().unwrap();
        let t2 = h_copy.resolve::<i32>().unwrap();

        // t and t2 may share the same memory, but it's difficuly to check
        // since the `resolve` does not guarantee that.
        assert_eq!(&t[..], &t2[..]);
    }

    #[cfg(feature = "tensorflow_gpu")]
    #[test]
    fn test_copy_to_device() {
        let opts = ContextOptions::new();
        let ctx = Context::new(opts).unwrap();
        let devices = ctx.device_list().unwrap();
        if !(devices.iter().any(|d| d.device_type == "GPU")) {
            eprintln!("Skipping test_copy_to_device because no GPU device is found");
            return;
        }

        let t = Tensor::new(&[2, 2]).with_values(&[0_i32, 1, 2, 3]).unwrap();
        let h = TensorHandle::new(&ctx, &t).unwrap();
        let target_device = "/job:localhost/replica:0/task:0/device:GPU:0";
        let h_gpu = TensorHandle::copy_to_device(&h, &ctx, target_device).unwrap();
        assert_eq!(h_gpu.device_name().unwrap(), target_device);
        let t2 = h_gpu.resolve::<i32>().unwrap();

        assert_eq!(&t[..], &t2[..]);
    }

    #[cfg(feature = "tensorflow_gpu")]
    #[test]
    fn test_copy_to_device_lifetime() {
        let values = [0_i32, 1, 2, 3];
        let target_device = "/job:localhost/replica:0/task:0/device:GPU:0";

        let opts = ContextOptions::new();
        let ctx = Context::new(opts).unwrap();
        let devices = ctx.device_list().unwrap();
        assert!(
            devices.iter().any(|d| d.device_type == "GPU"),
            "Skipp test_copy_to_device because no GPU device was found"
        );

        let h_gpu = {
            // Create a temporal Context
            let opts = ContextOptions::new();
            let ctx2 = Context::new(opts).unwrap();
            let t = Tensor::new(&[2, 2]).with_values(&values).unwrap();

            // Create a TensorHandle managed by the context `ctx2`.
            let h = TensorHandle::new(&ctx2, &t).unwrap();

            // Copy to GPU. This creates a new handle managed by the context `ctx`.
            h.copy_to_device(&ctx, target_device).unwrap()
        };
        assert_eq!(h_gpu.device_name().unwrap(), target_device);
        let t2 = h_gpu.resolve::<i32>().unwrap();

        assert_eq!(&values[..], &t2[..]);
    }
}
<|endoftext|>


<commit_before>/*!

Things for the construction and sending of HTTP requests.

If you want to make a request, `RequestWriter::new` is where you start, and
`RequestWriter.read_response` is where you will send the request and read the response.

```rust
extern crate http;
extern crate url;

use http::client::RequestWriter;
use http::method::Get;
use url::Url;

fn main() {
    let url = Url::parse("http://example.com/").unwrap();
    let request: RequestWriter = match RequestWriter::new(Get, url) {
        Ok(request) => request,
        Err(error) => fail!(":-( {}", error),
    };

    let mut response = match request.read_response() {
        Ok(response) => response,
        Err((_request, error)) => fail!(":-( {}", error),
    };
    // Now you have a `ResponseReader`; see http::client::response for docs on that.
}
```

If you wish to send a request body (e.g. POST requests), I'm sorry to have to tell you that there is
not *good* support for this yet. However, it can be done; here is an example:

```rust
# extern crate url;
# extern crate http;
# use http::client::RequestWriter;
# use http::method::Get;
# use url::Url;
# #[allow(unused_must_use)]
# fn main() {
# let url = Url::parse("http://example.com/").unwrap();
let data = b"var1=val1&var2=val2";
let mut request: RequestWriter = match RequestWriter::new(Get, url) {
    Ok(request) => request,
    Err(error) => fail!(":-( {}", error),
};

request.headers.content_length = Some(data.len());
request.write(data);
let response = match request.read_response() {
    Ok(response) => response,
    Err((_request, error)) => fail!(":-( {}", error),
};
# }
```

*/

use url::Url;
use method::Method;
use std::io::{IoError, IoResult};
use std::io::net::get_host_addresses;
use std::io::net::ip::{SocketAddr, Ipv4Addr};
use buffer::BufferedStream;
use headers::request::HeaderCollection;
use headers::host::Host;
use connecter::Connecter;

use client::response::ResponseReader;

/*impl ResponseReader {
    {
        let mut buf = [0u8, ..2000];
        match stream.read(buf) {
            None => fail!("Read error :-("),  // conditions for error interception, again
            Some(bytes_read) => {
                println!(str::from_bytes(buf.slice_to(bytes_read)));
            }
        }

        match response {
            Some(response) => Ok(response),
            None => Err(self),
        }
    }
}*/

pub struct RequestWriter<S = super::NetworkStream> {
    // The place to write to (typically a network stream, which is
    // io::net::tcp::TcpStream or an SSL wrapper around that)
    stream: Option<BufferedStream<S>>,
    headers_written: bool,

    /// The originating IP address of the request.
    pub remote_addr: Option<SocketAddr>,

    /// The host name and IP address that the request was sent to; this must always be specified for
    /// HTTP/1.1 requests (or the request will be rejected), but for HTTP/1.0 requests the Host
    /// header was not defined, and so this field will probably be None in such cases.
    //host: Host,  // Now headers.host

    /// The headers sent with the request.
    pub headers: Box<HeaderCollection>,

    /// The HTTP method for the request.
    pub method: Method,

    /// The URL being requested.
    pub url: Url,

    /// Should we use SSL?
    use_ssl: bool,
}

/// Low-level HTTP request writing support
///
/// Moderately hacky, and due to current limitations in the TcpStream arrangement reading cannot
/// take place until writing is completed.
///
/// At present, this only supports making one request per connection.
impl<S: Reader + Writer = super::NetworkStream> RequestWriter<S> {
    /// Create a `RequestWriter` writing to the specified location
    pub fn new(method: Method, url: Url) -> IoResult<RequestWriter<S>> {
        RequestWriter::new_request(method, url, false, true)
    }

    pub fn new_request(method: Method, url: Url, use_ssl: bool, auto_detect_ssl: bool) -> IoResult<RequestWriter<S>> {
        let host = Host {
            name: url.domain().unwrap().to_string(),
            port: {
                let port = url.port().unwrap();
                if port.is_empty() {
                    None
                } else {
                    Some(from_str(port).expect("You didn’t aught to give a bad port!"))
                }
            },
        };

        let remote_addr = try!(url_to_socket_addr(&url, &host));
        info!("using ip address {} for {}", remote_addr, host.name.as_slice());

        fn url_to_socket_addr(url: &Url, host: &Host) -> IoResult<SocketAddr> {
            // Just grab the first IPv4 address
            let addrs = try!(get_host_addresses(host.name.as_slice()));
            let addr = addrs.move_iter().find(|&a| {
                match a {
                    Ipv4Addr(..) => true,
                    _ => false
                }
            });

            // TODO: Error handling
            let addr = addr.unwrap();

            // Default to 80, using the port specified or 443 if the protocol is HTTPS.
            let port = match host.port {
                Some(p) => p,
                // FIXME: case insensitivity?
                None => if url.scheme.as_slice() == "https" { 443 } else { 80 },
            };

            Ok(SocketAddr {
                ip: addr,
                port: port
            })
        }

        let mut request = RequestWriter {
            stream: None,
            headers_written: false,
            remote_addr: Some(remote_addr),
            headers: box HeaderCollection::new(),
            method: method,
            url: url,
            use_ssl: use_ssl,
        };

        if auto_detect_ssl {
            // FIXME: case insensitivity?
            request.use_ssl = request.url.scheme.as_slice() == "https";
        }

        request.headers.host = Some(host);
        Ok(request)
    }
}

impl<S: Connecter + Reader + Writer = super::NetworkStream> RequestWriter<S> {

    /// Connect to the remote host if not already connected.
    pub fn try_connect(&mut self) -> IoResult<()> {
        if self.stream.is_none() {
            self.connect()
        } else {
            Ok(())
        }
    }

    /// Connect to the remote host; fails if already connected.
    /// Returns ``true`` upon success and ``false`` upon failure (also use conditions).
    pub fn connect(&mut self) -> IoResult<()> {
        if !self.stream.is_none() {
            fail!("I don't think you meant to call connect() twice, you know.");
        }

        self.stream = match self.remote_addr {
            Some(addr) => {
                let stream = try!(Connecter::connect(
                    addr, self.headers.host.as_ref().unwrap().name.as_slice(), self.use_ssl));
                Some(BufferedStream::new(stream))
            },
            None => fail!("connect() called before remote_addr was set"),
        };
        Ok(())
    }

    /// Write the Request-Line and headers of the response, if we have not already done so.
    pub fn try_write_headers(&mut self) -> IoResult<()> {
        if !self.headers_written {
            self.write_headers()
        } else {
            Ok(())
        }
    }

    /// Write the Status-Line and headers of the response, in preparation for writing the body.
    ///
    /// If the headers have already been written, this will fail. See also `try_write_headers`.
    pub fn write_headers(&mut self) -> IoResult<()> {
        // This marks the beginning of the response (RFC2616 §5)
        if self.headers_written {
            fail!("RequestWriter.write_headers() called, but headers already written");
        }
        if self.stream.is_none() {
            try!(self.connect());
        }

        // Write the Request-Line (RFC2616 §5.1)
        // TODO: get to the point where we can say HTTP/1.1 with good conscience
        let (question_mark, query) = match self.url.query {
            Some(ref query) => ("?", query.as_slice()),
            None => ("", "")
        };
        try!(write!(self.stream.get_mut_ref() as &mut Writer,
            "{} {}{}{} HTTP/1.0\r\n",
            self.method, self.url.serialize_path().unwrap(), question_mark, query));

        try!(self.headers.write_all(self.stream.get_mut_ref()));
        self.headers_written = true;
        Ok(())
    }

    /**
     * Send the request and construct a `ResponseReader` out of it.
     *
     * If the request sending fails in any way, a condition will be raised; if handled, the original
     * request will be returned as an `Err`.
     */
    pub fn read_response(mut self) -> Result<ResponseReader<S>, (RequestWriter<S>, IoError)> {
        match self.try_write_headers() {
            Ok(()) => (),
            Err(err) => return Err((self, err)),
        };
        match self.flush() {
            Ok(()) => (),
            Err(err) => return Err((self, err)),
        };
        match self.stream.take() {
            Some(stream) => ResponseReader::construct(stream, self),
            None => unreachable!(), // TODO: is it genuinely unreachable?
        }
    }
}

/// Write the request body. Note that any calls to `write()` will cause the headers to be sent.
impl<S: Reader + Writer + Connecter = super::NetworkStream> Writer for RequestWriter<S> {
    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
        if !self.headers_written {
            try!(self.write_headers());
        }
        // TODO: decide whether using get_mut_ref() is sound
        // (it will cause failure if None)
        self.stream.get_mut_ref().write(buf)
    }

    fn flush(&mut self) -> IoResult<()> {
        // TODO: ditto
        self.stream.get_mut_ref().flush()
    }
}
<commit_msg>rust-url breaking change: Url.port is now Option<16> rather than String<commit_after>/*!

Things for the construction and sending of HTTP requests.

If you want to make a request, `RequestWriter::new` is where you start, and
`RequestWriter.read_response` is where you will send the request and read the response.

```rust
extern crate http;
extern crate url;

use http::client::RequestWriter;
use http::method::Get;
use url::Url;

fn main() {
    let url = Url::parse("http://example.com/").unwrap();
    let request: RequestWriter = match RequestWriter::new(Get, url) {
        Ok(request) => request,
        Err(error) => fail!(":-( {}", error),
    };

    let mut response = match request.read_response() {
        Ok(response) => response,
        Err((_request, error)) => fail!(":-( {}", error),
    };
    // Now you have a `ResponseReader`; see http::client::response for docs on that.
}
```

If you wish to send a request body (e.g. POST requests), I'm sorry to have to tell you that there is
not *good* support for this yet. However, it can be done; here is an example:

```rust
# extern crate url;
# extern crate http;
# use http::client::RequestWriter;
# use http::method::Get;
# use url::Url;
# #[allow(unused_must_use)]
# fn main() {
# let url = Url::parse("http://example.com/").unwrap();
let data = b"var1=val1&var2=val2";
let mut request: RequestWriter = match RequestWriter::new(Get, url) {
    Ok(request) => request,
    Err(error) => fail!(":-( {}", error),
};

request.headers.content_length = Some(data.len());
request.write(data);
let response = match request.read_response() {
    Ok(response) => response,
    Err((_request, error)) => fail!(":-( {}", error),
};
# }
```

*/

use url::Url;
use method::Method;
use std::io::{IoError, IoResult};
use std::io::net::get_host_addresses;
use std::io::net::ip::{SocketAddr, Ipv4Addr};
use buffer::BufferedStream;
use headers::request::HeaderCollection;
use headers::host::Host;
use connecter::Connecter;

use client::response::ResponseReader;

/*impl ResponseReader {
    {
        let mut buf = [0u8, ..2000];
        match stream.read(buf) {
            None => fail!("Read error :-("),  // conditions for error interception, again
            Some(bytes_read) => {
                println!(str::from_bytes(buf.slice_to(bytes_read)));
            }
        }

        match response {
            Some(response) => Ok(response),
            None => Err(self),
        }
    }
}*/

pub struct RequestWriter<S = super::NetworkStream> {
    // The place to write to (typically a network stream, which is
    // io::net::tcp::TcpStream or an SSL wrapper around that)
    stream: Option<BufferedStream<S>>,
    headers_written: bool,

    /// The originating IP address of the request.
    pub remote_addr: Option<SocketAddr>,

    /// The host name and IP address that the request was sent to; this must always be specified for
    /// HTTP/1.1 requests (or the request will be rejected), but for HTTP/1.0 requests the Host
    /// header was not defined, and so this field will probably be None in such cases.
    //host: Host,  // Now headers.host

    /// The headers sent with the request.
    pub headers: Box<HeaderCollection>,

    /// The HTTP method for the request.
    pub method: Method,

    /// The URL being requested.
    pub url: Url,

    /// Should we use SSL?
    use_ssl: bool,
}

/// Low-level HTTP request writing support
///
/// Moderately hacky, and due to current limitations in the TcpStream arrangement reading cannot
/// take place until writing is completed.
///
/// At present, this only supports making one request per connection.
impl<S: Reader + Writer = super::NetworkStream> RequestWriter<S> {
    /// Create a `RequestWriter` writing to the specified location
    pub fn new(method: Method, url: Url) -> IoResult<RequestWriter<S>> {
        RequestWriter::new_request(method, url, false, true)
    }

    pub fn new_request(method: Method, url: Url, use_ssl: bool, auto_detect_ssl: bool) -> IoResult<RequestWriter<S>> {
        let host = Host {
            name: url.domain().unwrap().to_string(),
            port: url.port(),
        };

        let remote_addr = try!(url_to_socket_addr(&url, &host));
        info!("using ip address {} for {}", remote_addr, host.name.as_slice());

        fn url_to_socket_addr(url: &Url, host: &Host) -> IoResult<SocketAddr> {
            // Just grab the first IPv4 address
            let addrs = try!(get_host_addresses(host.name.as_slice()));
            let addr = addrs.move_iter().find(|&a| {
                match a {
                    Ipv4Addr(..) => true,
                    _ => false
                }
            });

            // TODO: Error handling
            let addr = addr.unwrap();

            // Default to 80, using the port specified or 443 if the protocol is HTTPS.
            let port = match host.port {
                Some(p) => p,
                // FIXME: case insensitivity?
                None => if url.scheme.as_slice() == "https" { 443 } else { 80 },
            };

            Ok(SocketAddr {
                ip: addr,
                port: port
            })
        }

        let mut request = RequestWriter {
            stream: None,
            headers_written: false,
            remote_addr: Some(remote_addr),
            headers: box HeaderCollection::new(),
            method: method,
            url: url,
            use_ssl: use_ssl,
        };

        if auto_detect_ssl {
            // FIXME: case insensitivity?
            request.use_ssl = request.url.scheme.as_slice() == "https";
        }

        request.headers.host = Some(host);
        Ok(request)
    }
}

impl<S: Connecter + Reader + Writer = super::NetworkStream> RequestWriter<S> {

    /// Connect to the remote host if not already connected.
    pub fn try_connect(&mut self) -> IoResult<()> {
        if self.stream.is_none() {
            self.connect()
        } else {
            Ok(())
        }
    }

    /// Connect to the remote host; fails if already connected.
    /// Returns ``true`` upon success and ``false`` upon failure (also use conditions).
    pub fn connect(&mut self) -> IoResult<()> {
        if !self.stream.is_none() {
            fail!("I don't think you meant to call connect() twice, you know.");
        }

        self.stream = match self.remote_addr {
            Some(addr) => {
                let stream = try!(Connecter::connect(
                    addr, self.headers.host.as_ref().unwrap().name.as_slice(), self.use_ssl));
                Some(BufferedStream::new(stream))
            },
            None => fail!("connect() called before remote_addr was set"),
        };
        Ok(())
    }

    /// Write the Request-Line and headers of the response, if we have not already done so.
    pub fn try_write_headers(&mut self) -> IoResult<()> {
        if !self.headers_written {
            self.write_headers()
        } else {
            Ok(())
        }
    }

    /// Write the Status-Line and headers of the response, in preparation for writing the body.
    ///
    /// If the headers have already been written, this will fail. See also `try_write_headers`.
    pub fn write_headers(&mut self) -> IoResult<()> {
        // This marks the beginning of the response (RFC2616 §5)
        if self.headers_written {
            fail!("RequestWriter.write_headers() called, but headers already written");
        }
        if self.stream.is_none() {
            try!(self.connect());
        }

        // Write the Request-Line (RFC2616 §5.1)
        // TODO: get to the point where we can say HTTP/1.1 with good conscience
        let (question_mark, query) = match self.url.query {
            Some(ref query) => ("?", query.as_slice()),
            None => ("", "")
        };
        try!(write!(self.stream.get_mut_ref() as &mut Writer,
            "{} {}{}{} HTTP/1.0\r\n",
            self.method, self.url.serialize_path().unwrap(), question_mark, query));

        try!(self.headers.write_all(self.stream.get_mut_ref()));
        self.headers_written = true;
        Ok(())
    }

    /**
     * Send the request and construct a `ResponseReader` out of it.
     *
     * If the request sending fails in any way, a condition will be raised; if handled, the original
     * request will be returned as an `Err`.
     */
    pub fn read_response(mut self) -> Result<ResponseReader<S>, (RequestWriter<S>, IoError)> {
        match self.try_write_headers() {
            Ok(()) => (),
            Err(err) => return Err((self, err)),
        };
        match self.flush() {
            Ok(()) => (),
            Err(err) => return Err((self, err)),
        };
        match self.stream.take() {
            Some(stream) => ResponseReader::construct(stream, self),
            None => unreachable!(), // TODO: is it genuinely unreachable?
        }
    }
}

/// Write the request body. Note that any calls to `write()` will cause the headers to be sent.
impl<S: Reader + Writer + Connecter = super::NetworkStream> Writer for RequestWriter<S> {
    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
        if !self.headers_written {
            try!(self.write_headers());
        }
        // TODO: decide whether using get_mut_ref() is sound
        // (it will cause failure if None)
        self.stream.get_mut_ref().write(buf)
    }

    fn flush(&mut self) -> IoResult<()> {
        // TODO: ditto
        self.stream.get_mut_ref().flush()
    }
}
<|endoftext|>


<commit_before>use std::time::Instant;
use std::env;

use interpreter::ast::*;
use interpreter::{Ident, DebugInfo};

use interpreter::builtins::register_builtins;

// Obviously just a rough sketch
// These fields will need to be rethought
#[derive(Clone, Debug, PartialEq, PartialOrd)]
pub struct ExpressionOutput {
    pub command: String,
    pub stdout: String,
    pub stderr: String,
    pub interleaved: String, //how it actually happened, stdout + stderr + any echoed stdin
    pub started: Instant,
    pub completed: Instant,
}

pub fn run_expression(
    buffer: &str,
    mut commandline: Vec<String>,
) -> Result<ExpressionOutput, String> {

    let mut expr = Expr::parse(buffer)?;

    let mut args = Vec::new();
    for arg in commandline.drain(..) {
        args.push(Expr::new(Node::Str(arg), DebugInfo::none()));
    }

    expr.memory.insert(
        Ident("$arg".to_string()),
        Expr::new(Node::Array(args), DebugInfo::none()),
    );

    // insert the environment variables
    for (key, value) in env::vars_os() {
        let (key, value) = (
            key.to_string_lossy().to_string(),
            value.to_string_lossy().to_string(),
        );
        let key = "$".to_string() + &key;
        if key == "$PATH" {
            let values = value
                .split(":")
                .map(|val| {
                    Expr::new(Node::Str(val.to_string()), DebugInfo::none())
                })
                .collect::<Vec<_>>();
            expr.memory.insert(
                Ident(key),
                Expr::new(Node::Array(values), DebugInfo::none()),
            );
        } else {
            expr.memory.insert(
                Ident(key),
                Expr::new(Node::Str(value), DebugInfo::none()),
            );
        }
    }

    register_builtins(&mut expr.memory);

    let started = Instant::now();
    let result = expr.run()?;
    let completed = Instant::now();

    let output = format!("{:?}", result.node);
    let output = if output == "Noop" {
        String::new()
    } else {
        output
    };

    println!("processing took {:?}", completed - started);

    Ok(ExpressionOutput {
        started,
        completed,
        command: buffer.to_string(),
        stdout: output.clone(),
        stderr: "".to_string(),
        interleaved: output,
    })
}
<commit_msg>a step towards catching panics, but it still prints the panic to the console for some reason<commit_after>use std::time::Instant;
use std::env;

use interpreter::ast::*;
use interpreter::{Ident, DebugInfo};

use interpreter::builtins::register_builtins;

// Obviously just a rough sketch
// These fields will need to be rethought
#[derive(Clone, Debug, PartialEq, PartialOrd)]
pub struct ExpressionOutput {
    pub command: String,
    pub stdout: String,
    pub stderr: String,
    pub interleaved: String, //how it actually happened, stdout + stderr + any echoed stdin
    pub started: Instant,
    pub completed: Instant,
}

pub fn run_expression(
    buffer: &str,
    mut commandline: Vec<String>,
) -> Result<ExpressionOutput, String> {

    let mut expr = Expr::parse(buffer)?;

    let mut args = Vec::new();
    for arg in commandline.drain(..) {
        args.push(Expr::new(Node::Str(arg), DebugInfo::none()));
    }

    expr.memory.insert(
        Ident("$arg".to_string()),
        Expr::new(Node::Array(args), DebugInfo::none()),
    );

    // insert the environment variables
    for (key, value) in env::vars_os() {
        let (key, value) = (
            key.to_string_lossy().to_string(),
            value.to_string_lossy().to_string(),
        );
        let key = "$".to_string() + &key;
        if key == "$PATH" {
            let values = value
                .split(":")
                .map(|val| {
                    Expr::new(Node::Str(val.to_string()), DebugInfo::none())
                })
                .collect::<Vec<_>>();
            expr.memory.insert(
                Ident(key),
                Expr::new(Node::Array(values), DebugInfo::none()),
            );
        } else {
            expr.memory.insert(
                Ident(key),
                Expr::new(Node::Str(value), DebugInfo::none()),
            );
        }
    }

    register_builtins(&mut expr.memory);

    let started = Instant::now();
    let result = match ::std::panic::catch_unwind(move || expr.run()) {
        Ok(result) => result?,
        Err(cause) => {
            if let Some(ref cause) = cause.downcast_ref::<String>() {
                Err(cause.to_string())?
            }
            panic!(cause);
        }
    };
    let completed = Instant::now();

    let output = format!("{:?}", result.node);
    let output = if output == "Noop" {
        String::new()
    } else {
        output
    };

    println!("processing took {:?}", completed - started);

    Ok(ExpressionOutput {
        started,
        completed,
        command: buffer.to_string(),
        stdout: output.clone(),
        stderr: "".to_string(),
        interleaved: output,
    })
}
<|endoftext|>