<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>Short-circuit more of compute_damage<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)|*);
            true
        } else {
            false
        }
    })
);

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 should check every CSS property, as enumerated in the fields of
    // http://doc.servo.org/style/properties/struct.ComputedValues.html

    // FIXME: Test somehow that every property is included.

    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
    ]) || 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, REFLOW_OUT_OF_FLOW ], [
        get_positionoffsets.top, get_positionoffsets.left,
        get_positionoffsets.right, get_positionoffsets.bottom
    ]) || 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
    ]);

    // 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());
    }

    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>use super::crt_objects::CrtObjectsFallback;
use super::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, TargetOptions, TlsModel};
use std::collections::BTreeMap;

pub fn options() -> TargetOptions {
    let mut lld_args = Vec::new();
    let mut clang_args = Vec::new();
    let mut arg = |arg: &str| {
        lld_args.push(arg.to_string());
        clang_args.push(format!("-Wl,{}", arg));
    };

    // There have been reports in the wild (rustwasm/wasm-bindgen#119) of
    // using threads causing weird hangs and bugs. Disable it entirely as
    // this isn't yet the bottleneck of compilation at all anyway.
    //
    // FIXME: we should file an upstream issue with LLD about this
    arg("--no-threads");

    // By default LLD only gives us one page of stack (64k) which is a
    // little small. Default to a larger stack closer to other PC platforms
    // (1MB) and users can always inject their own link-args to override this.
    arg("-z");
    arg("stack-size=1048576");

    // By default LLD's memory layout is:
    //
    // 1. First, a blank page
    // 2. Next, all static data
    // 3. Finally, the main stack (which grows down)
    //
    // This has the unfortunate consequence that on stack overflows you
    // corrupt static data and can cause some exceedingly weird bugs. To
    // help detect this a little sooner we instead request that the stack is
    // placed before static data.
    //
    // This means that we'll generate slightly larger binaries as references
    // to static data will take more bytes in the ULEB128 encoding, but
    // stack overflow will be guaranteed to trap as it underflows instead of
    // corrupting static data.
    arg("--stack-first");

    // FIXME we probably shouldn't pass this but instead pass an explicit list
    // of symbols we'll allow to be undefined. We don't currently have a
    // mechanism of knowing, however, which symbols are intended to be imported
    // from the environment and which are intended to be imported from other
    // objects linked elsewhere. This is a coarse approximation but is sure to
    // hide some bugs and frustrate someone at some point, so we should ideally
    // work towards a world where we can explicitly list symbols that are
    // supposed to be imported and have all other symbols generate errors if
    // they remain undefined.
    arg("--allow-undefined");

    // Rust code should never have warnings, and warnings are often
    // indicative of bugs, let's prevent them.
    arg("--fatal-warnings");

    // LLD only implements C++-like demangling, which doesn't match our own
    // mangling scheme. Tell LLD to not demangle anything and leave it up to
    // us to demangle these symbols later. Currently rustc does not perform
    // further demangling, but tools like twiggy and wasm-bindgen are intended
    // to do so.
    arg("--no-demangle");

    // The symbol visibility story is a bit in flux right now with LLD.
    // It's... not entirely clear to me what's going on, but this looks to
    // make everything work when `export_symbols` isn't otherwise called for
    // things like executables.
    //
    // This is really only here to get things working. If it can be removed and
    // basic tests still work, then sounds like it should be removed!
    arg("--export-dynamic");

    let mut pre_link_args = BTreeMap::new();
    pre_link_args.insert(LinkerFlavor::Lld(LldFlavor::Wasm), lld_args);
    pre_link_args.insert(LinkerFlavor::Gcc, clang_args);

    TargetOptions {
        // we allow dynamic linking, but only cdylibs. Basically we allow a
        // final library artifact that exports some symbols (a wasm module) but
        // we don't allow intermediate `dylib` crate types
        dynamic_linking: true,
        only_cdylib: true,

        // This means we'll just embed a `#[start]` function in the wasm module
        executables: true,

        // relatively self-explanatory!
        exe_suffix: ".wasm".to_string(),
        dll_prefix: String::new(),
        dll_suffix: ".wasm".to_string(),
        linker_is_gnu: false,

        max_atomic_width: Some(64),

        // Unwinding doesn't work right now, so the whole target unconditionally
        // defaults to panic=abort. Note that this is guaranteed to change in
        // the future once unwinding is implemented. Don't rely on this as we're
        // basically guaranteed to change it once WebAssembly supports
        // exceptions.
        panic_strategy: PanicStrategy::Abort,

        // Wasm doesn't have atomics yet, so tell LLVM that we're in a single
        // threaded model which will legalize atomics to normal operations.
        singlethread: true,

        // no dynamic linking, no need for default visibility!
        default_hidden_visibility: true,

        // Symbol visibility takes care of this for the WebAssembly.
        // Additionally the only known linker, LLD, doesn't support the script
        // arguments just yet
        limit_rdylib_exports: false,

        // we use the LLD shipped with the Rust toolchain by default
        linker: Some("rust-lld".to_owned()),
        lld_flavor: LldFlavor::Wasm,

        // No need for indirection here, simd types can always be passed by
        // value as the whole module either has simd or not, which is different
        // from x86 (for example) where programs can have functions that don't
        // enable simd features.
        simd_types_indirect: false,

        pre_link_args,

        crt_objects_fallback: Some(CrtObjectsFallback::Wasm),

        // This has no effect in LLVM 8 or prior, but in LLVM 9 and later when
        // PIC code is implemented this has quite a drastric effect if it stays
        // at the default, `pic`. In an effort to keep wasm binaries as minimal
        // as possible we're defaulting to `static` for now, but the hope is
        // that eventually we can ship a `pic`-compatible standard library which
        // works with `static` as well (or works with some method of generating
        // non-relative calls and such later on).
        relocation_model: RelocModel::Static,

        // When the atomics feature is activated then these two keys matter,
        // otherwise they're basically ignored by the standard library. In this
        // mode, however, the `#[thread_local]` attribute works (i.e.
        // `has_elf_tls`) and we need to get it to work by specifying
        // `local-exec` as that's all that's implemented in LLVM today for wasm.
        has_elf_tls: true,
        tls_model: TlsModel::LocalExec,

        // gdb scripts don't work on wasm blobs
        emit_debug_gdb_scripts: false,

        ..Default::default()
    }
}
<commit_msg>Auto merge of #75161 - sunfishcode:wasm-no-threads, r=alexcrichton<commit_after>use super::crt_objects::CrtObjectsFallback;
use super::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, TargetOptions, TlsModel};
use std::collections::BTreeMap;

pub fn options() -> TargetOptions {
    let mut lld_args = Vec::new();
    let mut clang_args = Vec::new();
    let mut arg = |arg: &str| {
        lld_args.push(arg.to_string());
        clang_args.push(format!("-Wl,{}", arg));
    };

    // By default LLD only gives us one page of stack (64k) which is a
    // little small. Default to a larger stack closer to other PC platforms
    // (1MB) and users can always inject their own link-args to override this.
    arg("-z");
    arg("stack-size=1048576");

    // By default LLD's memory layout is:
    //
    // 1. First, a blank page
    // 2. Next, all static data
    // 3. Finally, the main stack (which grows down)
    //
    // This has the unfortunate consequence that on stack overflows you
    // corrupt static data and can cause some exceedingly weird bugs. To
    // help detect this a little sooner we instead request that the stack is
    // placed before static data.
    //
    // This means that we'll generate slightly larger binaries as references
    // to static data will take more bytes in the ULEB128 encoding, but
    // stack overflow will be guaranteed to trap as it underflows instead of
    // corrupting static data.
    arg("--stack-first");

    // FIXME we probably shouldn't pass this but instead pass an explicit list
    // of symbols we'll allow to be undefined. We don't currently have a
    // mechanism of knowing, however, which symbols are intended to be imported
    // from the environment and which are intended to be imported from other
    // objects linked elsewhere. This is a coarse approximation but is sure to
    // hide some bugs and frustrate someone at some point, so we should ideally
    // work towards a world where we can explicitly list symbols that are
    // supposed to be imported and have all other symbols generate errors if
    // they remain undefined.
    arg("--allow-undefined");

    // Rust code should never have warnings, and warnings are often
    // indicative of bugs, let's prevent them.
    arg("--fatal-warnings");

    // LLD only implements C++-like demangling, which doesn't match our own
    // mangling scheme. Tell LLD to not demangle anything and leave it up to
    // us to demangle these symbols later. Currently rustc does not perform
    // further demangling, but tools like twiggy and wasm-bindgen are intended
    // to do so.
    arg("--no-demangle");

    // The symbol visibility story is a bit in flux right now with LLD.
    // It's... not entirely clear to me what's going on, but this looks to
    // make everything work when `export_symbols` isn't otherwise called for
    // things like executables.
    //
    // This is really only here to get things working. If it can be removed and
    // basic tests still work, then sounds like it should be removed!
    arg("--export-dynamic");

    let mut pre_link_args = BTreeMap::new();
    pre_link_args.insert(LinkerFlavor::Lld(LldFlavor::Wasm), lld_args);
    pre_link_args.insert(LinkerFlavor::Gcc, clang_args);

    TargetOptions {
        // we allow dynamic linking, but only cdylibs. Basically we allow a
        // final library artifact that exports some symbols (a wasm module) but
        // we don't allow intermediate `dylib` crate types
        dynamic_linking: true,
        only_cdylib: true,

        // This means we'll just embed a `#[start]` function in the wasm module
        executables: true,

        // relatively self-explanatory!
        exe_suffix: ".wasm".to_string(),
        dll_prefix: String::new(),
        dll_suffix: ".wasm".to_string(),
        linker_is_gnu: false,

        max_atomic_width: Some(64),

        // Unwinding doesn't work right now, so the whole target unconditionally
        // defaults to panic=abort. Note that this is guaranteed to change in
        // the future once unwinding is implemented. Don't rely on this as we're
        // basically guaranteed to change it once WebAssembly supports
        // exceptions.
        panic_strategy: PanicStrategy::Abort,

        // Wasm doesn't have atomics yet, so tell LLVM that we're in a single
        // threaded model which will legalize atomics to normal operations.
        singlethread: true,

        // no dynamic linking, no need for default visibility!
        default_hidden_visibility: true,

        // Symbol visibility takes care of this for the WebAssembly.
        // Additionally the only known linker, LLD, doesn't support the script
        // arguments just yet
        limit_rdylib_exports: false,

        // we use the LLD shipped with the Rust toolchain by default
        linker: Some("rust-lld".to_owned()),
        lld_flavor: LldFlavor::Wasm,

        // No need for indirection here, simd types can always be passed by
        // value as the whole module either has simd or not, which is different
        // from x86 (for example) where programs can have functions that don't
        // enable simd features.
        simd_types_indirect: false,

        pre_link_args,

        crt_objects_fallback: Some(CrtObjectsFallback::Wasm),

        // This has no effect in LLVM 8 or prior, but in LLVM 9 and later when
        // PIC code is implemented this has quite a drastric effect if it stays
        // at the default, `pic`. In an effort to keep wasm binaries as minimal
        // as possible we're defaulting to `static` for now, but the hope is
        // that eventually we can ship a `pic`-compatible standard library which
        // works with `static` as well (or works with some method of generating
        // non-relative calls and such later on).
        relocation_model: RelocModel::Static,

        // When the atomics feature is activated then these two keys matter,
        // otherwise they're basically ignored by the standard library. In this
        // mode, however, the `#[thread_local]` attribute works (i.e.
        // `has_elf_tls`) and we need to get it to work by specifying
        // `local-exec` as that's all that's implemented in LLVM today for wasm.
        has_elf_tls: true,
        tls_model: TlsModel::LocalExec,

        // gdb scripts don't work on wasm blobs
        emit_debug_gdb_scripts: false,

        ..Default::default()
    }
}
<|endoftext|>


<commit_before>use std::{collections::BTreeMap, convert::TryFrom};

use ruma::{
    events::{
        from_raw_json_value,
        pdu::{EventHash, Pdu, PduStub},
        room::member::{MemberEventContent, MembershipState},
        EventDeHelper, EventType,
    },
    identifiers::{EventId, RoomId, ServerName, UserId},
};
use serde::{de, Serialize};
use serde_json::value::RawValue as RawJsonValue;
use std::time::SystemTime;

pub struct Requester<'a> {
    pub prev_event_ids: Vec<EventId>,
    pub room_id: &'a RoomId,
    pub content: &'a serde_json::Value,
    pub state_key: Option<String>,
    pub sender: &'a UserId,
}

#[derive(Clone, Debug, Serialize)]
#[serde(untagged)]
pub enum StateEvent {
    Full(Pdu),
    Sync(PduStub),
}

impl StateEvent {
    pub fn to_requester(&self) -> Requester<'_> {
        Requester {
            prev_event_ids: self.prev_event_ids(),
            room_id: self.room_id().unwrap(),
            content: self.content(),
            state_key: self.state_key(),
            sender: self.sender(),
        }
    }

    pub fn is_power_event(&self) -> bool {
        match self {
            Self::Full(any_event) => match any_event {
                Pdu::RoomV1Pdu(event) => match event.kind {
                    EventType::RoomPowerLevels
                    | EventType::RoomJoinRules
                    | EventType::RoomCreate => event.state_key == Some("".into()),
                    EventType::RoomMember => {
                        if let Ok(content) =
                            // TODO fix clone
                            serde_json::from_value::<MemberEventContent>(event.content.clone())
                        {
                            if [MembershipState::Leave, MembershipState::Ban]
                                .contains(&content.membership)
                            {
                                return event.sender.as_str()
                                    // TODO is None here a failure
                                    != event.state_key.as_deref().unwrap_or("NOT A STATE KEY");
                            }
                        }

                        false
                    }
                    _ => false,
                },
                Pdu::RoomV3Pdu(event) => event.state_key == Some("".into()),
            },
            Self::Sync(any_event) => match any_event {
                PduStub::RoomV1PduStub(event) => match event.kind {
                    EventType::RoomPowerLevels
                    | EventType::RoomJoinRules
                    | EventType::RoomCreate => event.state_key == Some("".into()),
                    EventType::RoomMember => {
                        if let Ok(content) =
                            serde_json::from_value::<MemberEventContent>(event.content.clone())
                        {
                            if [MembershipState::Leave, MembershipState::Ban]
                                .contains(&content.membership)
                            {
                                return event.sender.as_str()
                                    // TODO does None here mean the same as state_key = ""
                                    != event.state_key.as_deref().unwrap_or("");
                            }
                        }

                        false
                    }
                    _ => false,
                },
                PduStub::RoomV3PduStub(event) => event.state_key == Some("".into()),
            },
        }
    }
    pub fn deserialize_content<C: serde::de::DeserializeOwned>(
        &self,
    ) -> Result<C, serde_json::Error> {
        match self {
            Self::Full(ev) => match ev {
                Pdu::RoomV1Pdu(ev) => serde_json::from_value(ev.content.clone()),
                Pdu::RoomV3Pdu(ev) => serde_json::from_value(ev.content.clone()),
            },
            Self::Sync(ev) => match ev {
                PduStub::RoomV1PduStub(ev) => serde_json::from_value(ev.content.clone()),
                PduStub::RoomV3PduStub(ev) => serde_json::from_value(ev.content.clone()),
            },
        }
    }
    pub fn origin_server_ts(&self) -> &SystemTime {
        match self {
            Self::Full(ev) => match ev {
                Pdu::RoomV1Pdu(ev) => &ev.origin_server_ts,
                Pdu::RoomV3Pdu(ev) => &ev.origin_server_ts,
            },
            Self::Sync(ev) => match ev {
                PduStub::RoomV1PduStub(ev) => &ev.origin_server_ts,
                PduStub::RoomV3PduStub(ev) => &ev.origin_server_ts,
            },
        }
    }
    pub fn event_id(&self) -> EventId {
        match self {
            Self::Full(ev) => match ev {
                Pdu::RoomV1Pdu(ev) => ev.event_id.clone(),
                Pdu::RoomV3Pdu(_) => EventId::try_from(&*format!(
                    "${}",
                    ruma::signatures::reference_hash(
                        &serde_json::to_value(&ev).expect("event is valid, we just created it")
                    )
                    .expect("ruma can calculate reference hashes")
                ))
                .expect("ruma's reference hashes are valid event ids"),
            },
            Self::Sync(ev) => EventId::try_from(&*format!(
                "${}",
                ruma::signatures::reference_hash(
                    &serde_json::to_value(&ev).expect("event is valid, we just created it")
                )
                .expect("ruma can calculate reference hashes")
            ))
            .expect("ruma's reference hashes are valid event ids"),
        }
    }

    pub fn sender(&self) -> &UserId {
        match self {
            Self::Full(ev) => match ev {
                Pdu::RoomV1Pdu(ev) => &ev.sender,
                Pdu::RoomV3Pdu(ev) => &ev.sender,
            },
            Self::Sync(ev) => match ev {
                PduStub::RoomV1PduStub(ev) => &ev.sender,
                PduStub::RoomV3PduStub(ev) => &ev.sender,
            },
        }
    }

    pub fn redacts(&self) -> Option<&EventId> {
        match self {
            Self::Full(ev) => match ev {
                Pdu::RoomV1Pdu(ev) => ev.redacts.as_ref(),
                Pdu::RoomV3Pdu(ev) => ev.redacts.as_ref(),
            },
            Self::Sync(ev) => match ev {
                PduStub::RoomV1PduStub(ev) => ev.redacts.as_ref(),
                PduStub::RoomV3PduStub(ev) => ev.redacts.as_ref(),
            },
        }
    }

    pub fn room_id(&self) -> Option<&RoomId> {
        match self {
            Self::Full(ev) => match ev {
                Pdu::RoomV1Pdu(ev) => Some(&ev.room_id),
                Pdu::RoomV3Pdu(ev) => Some(&ev.room_id),
            },
            Self::Sync(_) => None,
        }
    }
    pub fn kind(&self) -> EventType {
        match self {
            Self::Full(ev) => match ev {
                Pdu::RoomV1Pdu(ev) => ev.kind.clone(),
                Pdu::RoomV3Pdu(ev) => ev.kind.clone(),
            },
            Self::Sync(ev) => match ev {
                PduStub::RoomV1PduStub(ev) => ev.kind.clone(),
                PduStub::RoomV3PduStub(ev) => ev.kind.clone(),
            },
        }
    }
    pub fn state_key(&self) -> Option<String> {
        match self {
            Self::Full(ev) => match ev {
                Pdu::RoomV1Pdu(ev) => ev.state_key.clone(),
                Pdu::RoomV3Pdu(ev) => ev.state_key.clone(),
            },
            Self::Sync(ev) => match ev {
                PduStub::RoomV1PduStub(ev) => ev.state_key.clone(),
                PduStub::RoomV3PduStub(ev) => ev.state_key.clone(),
            },
        }
    }
    pub fn origin(&self) -> String {
        match self {
            Self::Full(ev) => match ev {
                Pdu::RoomV1Pdu(ev) => ev.origin.clone(),
                Pdu::RoomV3Pdu(ev) => ev.origin.clone(),
            },
            Self::Sync(ev) => match ev {
                PduStub::RoomV1PduStub(ev) => ev.origin.clone(),
                PduStub::RoomV3PduStub(ev) => ev.origin.clone(),
            },
        }
    }

    pub fn prev_event_ids(&self) -> Vec<EventId> {
        match self {
            Self::Full(ev) => match ev {
                Pdu::RoomV1Pdu(ev) => ev.prev_events.iter().map(|(id, _)| id).cloned().collect(),
                Pdu::RoomV3Pdu(ev) => ev.prev_events.clone(),
            },
            Self::Sync(ev) => match ev {
                PduStub::RoomV1PduStub(ev) => {
                    ev.prev_events.iter().map(|(id, _)| id).cloned().collect()
                }
                PduStub::RoomV3PduStub(ev) => ev.prev_events.to_vec(),
            },
        }
    }

    pub fn auth_events(&self) -> Vec<EventId> {
        match self {
            Self::Full(ev) => match ev {
                Pdu::RoomV1Pdu(ev) => ev.auth_events.iter().map(|(id, _)| id).cloned().collect(),
                Pdu::RoomV3Pdu(ev) => ev.auth_events.to_vec(),
            },
            Self::Sync(ev) => match ev {
                PduStub::RoomV1PduStub(ev) => {
                    ev.auth_events.iter().map(|(id, _)| id).cloned().collect()
                }
                PduStub::RoomV3PduStub(ev) => ev.auth_events.to_vec(),
            },
        }
    }

    pub fn content(&self) -> &serde_json::Value {
        match self {
            Self::Full(ev) => match ev {
                Pdu::RoomV1Pdu(ev) => &ev.content,
                Pdu::RoomV3Pdu(ev) => &ev.content,
            },
            Self::Sync(ev) => match ev {
                PduStub::RoomV1PduStub(ev) => &ev.content,
                PduStub::RoomV3PduStub(ev) => &ev.content,
            },
        }
    }

    pub fn unsigned(&self) -> &BTreeMap<String, serde_json::Value> {
        // CONFIRM: The only way this would fail is if we got bad json, it should fail in ruma
        // before it fails here.
        match self {
            Self::Full(ev) => match ev {
                Pdu::RoomV1Pdu(ev) => &ev.unsigned,
                Pdu::RoomV3Pdu(ev) => &ev.unsigned,
            },
            Self::Sync(ev) => match ev {
                PduStub::RoomV1PduStub(ev) => &ev.unsigned,
                PduStub::RoomV3PduStub(ev) => &ev.unsigned,
            },
        }
    }

    pub fn signatures(&self) -> BTreeMap<Box<ServerName>, BTreeMap<ruma::ServerKeyId, String>> {
        match self {
            Self::Full(ev) => match ev {
                Pdu::RoomV1Pdu(_) => maplit::btreemap! {},
                Pdu::RoomV3Pdu(ev) => ev.signatures.clone(),
            },
            Self::Sync(ev) => match ev {
                PduStub::RoomV1PduStub(ev) => ev.signatures.clone(),
                PduStub::RoomV3PduStub(ev) => ev.signatures.clone(),
            },
        }
    }

    pub fn hashes(&self) -> &EventHash {
        match self {
            Self::Full(ev) => match ev {
                Pdu::RoomV1Pdu(ev) => &ev.hashes,
                Pdu::RoomV3Pdu(ev) => &ev.hashes,
            },
            Self::Sync(ev) => match ev {
                PduStub::RoomV1PduStub(ev) => &ev.hashes,
                PduStub::RoomV3PduStub(ev) => &ev.hashes,
            },
        }
    }

    pub fn is_type_and_key(&self, ev_type: EventType, state_key: &str) -> bool {
        match self {
            Self::Full(ev) => match ev {
                Pdu::RoomV1Pdu(ev) => {
                    ev.kind == ev_type && ev.state_key.as_deref() == Some(state_key)
                }
                Pdu::RoomV3Pdu(ev) => {
                    ev.kind == ev_type && ev.state_key.as_deref() == Some(state_key)
                }
            },
            Self::Sync(ev) => match ev {
                PduStub::RoomV1PduStub(ev) => {
                    ev.kind == ev_type && ev.state_key.as_deref() == Some(state_key)
                }
                PduStub::RoomV3PduStub(ev) => {
                    ev.kind == ev_type && ev.state_key.as_deref() == Some(state_key)
                }
            },
        }
    }
}

impl<'de> de::Deserialize<'de> for StateEvent {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: de::Deserializer<'de>,
    {
        let json = Box::<RawJsonValue>::deserialize(deserializer)?;
        let EventDeHelper {
            room_id, unsigned, ..
        } = from_raw_json_value(&json)?;

        // Determine whether the event is a full or sync
        // based on the fields present.
        if room_id.is_some() {
            Ok(match unsigned {
                Some(unsigned) if unsigned.redacted_because.is_some() => {
                    panic!("TODO deal with redacted events")
                }
                _ => StateEvent::Full(Pdu::RoomV1Pdu(from_raw_json_value(&json)?)),
            })
        } else {
            Ok(match unsigned {
                Some(unsigned) if unsigned.redacted_because.is_some() => {
                    panic!("TODO deal with redacted events")
                }
                _ => StateEvent::Sync(from_raw_json_value(&json)?),
            })
        }
    }
}
<commit_msg>Add depth getter to StateEvent<commit_after>use std::{collections::BTreeMap, convert::TryFrom};

use js_int::UInt;
use ruma::{
    events::{
        from_raw_json_value,
        pdu::{EventHash, Pdu, PduStub},
        room::member::{MemberEventContent, MembershipState},
        EventDeHelper, EventType,
    },
    identifiers::{EventId, RoomId, ServerName, UserId},
};
use serde::{de, Serialize};
use serde_json::value::RawValue as RawJsonValue;
use std::time::SystemTime;

pub struct Requester<'a> {
    pub prev_event_ids: Vec<EventId>,
    pub room_id: &'a RoomId,
    pub content: &'a serde_json::Value,
    pub state_key: Option<String>,
    pub sender: &'a UserId,
}

#[derive(Clone, Debug, Serialize)]
#[serde(untagged)]
pub enum StateEvent {
    Full(Pdu),
    Sync(PduStub),
}

impl StateEvent {
    pub fn to_requester(&self) -> Requester<'_> {
        Requester {
            prev_event_ids: self.prev_event_ids(),
            room_id: self.room_id().unwrap(),
            content: self.content(),
            state_key: self.state_key(),
            sender: self.sender(),
        }
    }

    pub fn is_power_event(&self) -> bool {
        match self {
            Self::Full(any_event) => match any_event {
                Pdu::RoomV1Pdu(event) => match event.kind {
                    EventType::RoomPowerLevels
                    | EventType::RoomJoinRules
                    | EventType::RoomCreate => event.state_key == Some("".into()),
                    EventType::RoomMember => {
                        if let Ok(content) =
                            // TODO fix clone
                            serde_json::from_value::<MemberEventContent>(event.content.clone())
                        {
                            if [MembershipState::Leave, MembershipState::Ban]
                                .contains(&content.membership)
                            {
                                return event.sender.as_str()
                                    // TODO is None here a failure
                                    != event.state_key.as_deref().unwrap_or("NOT A STATE KEY");
                            }
                        }

                        false
                    }
                    _ => false,
                },
                Pdu::RoomV3Pdu(event) => event.state_key == Some("".into()),
            },
            Self::Sync(any_event) => match any_event {
                PduStub::RoomV1PduStub(event) => match event.kind {
                    EventType::RoomPowerLevels
                    | EventType::RoomJoinRules
                    | EventType::RoomCreate => event.state_key == Some("".into()),
                    EventType::RoomMember => {
                        if let Ok(content) =
                            serde_json::from_value::<MemberEventContent>(event.content.clone())
                        {
                            if [MembershipState::Leave, MembershipState::Ban]
                                .contains(&content.membership)
                            {
                                return event.sender.as_str()
                                    // TODO does None here mean the same as state_key = ""
                                    != event.state_key.as_deref().unwrap_or("");
                            }
                        }

                        false
                    }
                    _ => false,
                },
                PduStub::RoomV3PduStub(event) => event.state_key == Some("".into()),
            },
        }
    }
    pub fn deserialize_content<C: serde::de::DeserializeOwned>(
        &self,
    ) -> Result<C, serde_json::Error> {
        match self {
            Self::Full(ev) => match ev {
                Pdu::RoomV1Pdu(ev) => serde_json::from_value(ev.content.clone()),
                Pdu::RoomV3Pdu(ev) => serde_json::from_value(ev.content.clone()),
            },
            Self::Sync(ev) => match ev {
                PduStub::RoomV1PduStub(ev) => serde_json::from_value(ev.content.clone()),
                PduStub::RoomV3PduStub(ev) => serde_json::from_value(ev.content.clone()),
            },
        }
    }
    pub fn origin_server_ts(&self) -> &SystemTime {
        match self {
            Self::Full(ev) => match ev {
                Pdu::RoomV1Pdu(ev) => &ev.origin_server_ts,
                Pdu::RoomV3Pdu(ev) => &ev.origin_server_ts,
            },
            Self::Sync(ev) => match ev {
                PduStub::RoomV1PduStub(ev) => &ev.origin_server_ts,
                PduStub::RoomV3PduStub(ev) => &ev.origin_server_ts,
            },
        }
    }
    pub fn event_id(&self) -> EventId {
        match self {
            Self::Full(ev) => match ev {
                Pdu::RoomV1Pdu(ev) => ev.event_id.clone(),
                Pdu::RoomV3Pdu(_) => EventId::try_from(&*format!(
                    "${}",
                    ruma::signatures::reference_hash(
                        &serde_json::to_value(&ev).expect("event is valid, we just created it")
                    )
                    .expect("ruma can calculate reference hashes")
                ))
                .expect("ruma's reference hashes are valid event ids"),
            },
            Self::Sync(ev) => EventId::try_from(&*format!(
                "${}",
                ruma::signatures::reference_hash(
                    &serde_json::to_value(&ev).expect("event is valid, we just created it")
                )
                .expect("ruma can calculate reference hashes")
            ))
            .expect("ruma's reference hashes are valid event ids"),
        }
    }

    pub fn sender(&self) -> &UserId {
        match self {
            Self::Full(ev) => match ev {
                Pdu::RoomV1Pdu(ev) => &ev.sender,
                Pdu::RoomV3Pdu(ev) => &ev.sender,
            },
            Self::Sync(ev) => match ev {
                PduStub::RoomV1PduStub(ev) => &ev.sender,
                PduStub::RoomV3PduStub(ev) => &ev.sender,
            },
        }
    }

    pub fn redacts(&self) -> Option<&EventId> {
        match self {
            Self::Full(ev) => match ev {
                Pdu::RoomV1Pdu(ev) => ev.redacts.as_ref(),
                Pdu::RoomV3Pdu(ev) => ev.redacts.as_ref(),
            },
            Self::Sync(ev) => match ev {
                PduStub::RoomV1PduStub(ev) => ev.redacts.as_ref(),
                PduStub::RoomV3PduStub(ev) => ev.redacts.as_ref(),
            },
        }
    }

    pub fn room_id(&self) -> Option<&RoomId> {
        match self {
            Self::Full(ev) => match ev {
                Pdu::RoomV1Pdu(ev) => Some(&ev.room_id),
                Pdu::RoomV3Pdu(ev) => Some(&ev.room_id),
            },
            Self::Sync(_) => None,
        }
    }
    pub fn kind(&self) -> EventType {
        match self {
            Self::Full(ev) => match ev {
                Pdu::RoomV1Pdu(ev) => ev.kind.clone(),
                Pdu::RoomV3Pdu(ev) => ev.kind.clone(),
            },
            Self::Sync(ev) => match ev {
                PduStub::RoomV1PduStub(ev) => ev.kind.clone(),
                PduStub::RoomV3PduStub(ev) => ev.kind.clone(),
            },
        }
    }
    pub fn state_key(&self) -> Option<String> {
        match self {
            Self::Full(ev) => match ev {
                Pdu::RoomV1Pdu(ev) => ev.state_key.clone(),
                Pdu::RoomV3Pdu(ev) => ev.state_key.clone(),
            },
            Self::Sync(ev) => match ev {
                PduStub::RoomV1PduStub(ev) => ev.state_key.clone(),
                PduStub::RoomV3PduStub(ev) => ev.state_key.clone(),
            },
        }
    }
    pub fn origin(&self) -> String {
        match self {
            Self::Full(ev) => match ev {
                Pdu::RoomV1Pdu(ev) => ev.origin.clone(),
                Pdu::RoomV3Pdu(ev) => ev.origin.clone(),
            },
            Self::Sync(ev) => match ev {
                PduStub::RoomV1PduStub(ev) => ev.origin.clone(),
                PduStub::RoomV3PduStub(ev) => ev.origin.clone(),
            },
        }
    }

    pub fn prev_event_ids(&self) -> Vec<EventId> {
        match self {
            Self::Full(ev) => match ev {
                Pdu::RoomV1Pdu(ev) => ev.prev_events.iter().map(|(id, _)| id).cloned().collect(),
                Pdu::RoomV3Pdu(ev) => ev.prev_events.clone(),
            },
            Self::Sync(ev) => match ev {
                PduStub::RoomV1PduStub(ev) => {
                    ev.prev_events.iter().map(|(id, _)| id).cloned().collect()
                }
                PduStub::RoomV3PduStub(ev) => ev.prev_events.to_vec(),
            },
        }
    }

    pub fn auth_events(&self) -> Vec<EventId> {
        match self {
            Self::Full(ev) => match ev {
                Pdu::RoomV1Pdu(ev) => ev.auth_events.iter().map(|(id, _)| id).cloned().collect(),
                Pdu::RoomV3Pdu(ev) => ev.auth_events.to_vec(),
            },
            Self::Sync(ev) => match ev {
                PduStub::RoomV1PduStub(ev) => {
                    ev.auth_events.iter().map(|(id, _)| id).cloned().collect()
                }
                PduStub::RoomV3PduStub(ev) => ev.auth_events.to_vec(),
            },
        }
    }

    pub fn content(&self) -> &serde_json::Value {
        match self {
            Self::Full(ev) => match ev {
                Pdu::RoomV1Pdu(ev) => &ev.content,
                Pdu::RoomV3Pdu(ev) => &ev.content,
            },
            Self::Sync(ev) => match ev {
                PduStub::RoomV1PduStub(ev) => &ev.content,
                PduStub::RoomV3PduStub(ev) => &ev.content,
            },
        }
    }

    pub fn unsigned(&self) -> &BTreeMap<String, serde_json::Value> {
        // CONFIRM: The only way this would fail is if we got bad json, it should fail in ruma
        // before it fails here.
        match self {
            Self::Full(ev) => match ev {
                Pdu::RoomV1Pdu(ev) => &ev.unsigned,
                Pdu::RoomV3Pdu(ev) => &ev.unsigned,
            },
            Self::Sync(ev) => match ev {
                PduStub::RoomV1PduStub(ev) => &ev.unsigned,
                PduStub::RoomV3PduStub(ev) => &ev.unsigned,
            },
        }
    }

    pub fn signatures(&self) -> BTreeMap<Box<ServerName>, BTreeMap<ruma::ServerKeyId, String>> {
        match self {
            Self::Full(ev) => match ev {
                Pdu::RoomV1Pdu(_) => maplit::btreemap! {},
                Pdu::RoomV3Pdu(ev) => ev.signatures.clone(),
            },
            Self::Sync(ev) => match ev {
                PduStub::RoomV1PduStub(ev) => ev.signatures.clone(),
                PduStub::RoomV3PduStub(ev) => ev.signatures.clone(),
            },
        }
    }

    pub fn hashes(&self) -> &EventHash {
        match self {
            Self::Full(ev) => match ev {
                Pdu::RoomV1Pdu(ev) => &ev.hashes,
                Pdu::RoomV3Pdu(ev) => &ev.hashes,
            },
            Self::Sync(ev) => match ev {
                PduStub::RoomV1PduStub(ev) => &ev.hashes,
                PduStub::RoomV3PduStub(ev) => &ev.hashes,
            },
        }
    }

    pub fn depth(&self) -> &UInt {
        match self {
            Self::Full(ev) => match ev {
                Pdu::RoomV1Pdu(ev) => &ev.depth,
                Pdu::RoomV3Pdu(ev) => &ev.depth,
            },
            Self::Sync(ev) => match ev {
                PduStub::RoomV1PduStub(ev) => &ev.depth,
                PduStub::RoomV3PduStub(ev) => &ev.depth,
            },
        }
    }

    pub fn is_type_and_key(&self, ev_type: EventType, state_key: &str) -> bool {
        match self {
            Self::Full(ev) => match ev {
                Pdu::RoomV1Pdu(ev) => {
                    ev.kind == ev_type && ev.state_key.as_deref() == Some(state_key)
                }
                Pdu::RoomV3Pdu(ev) => {
                    ev.kind == ev_type && ev.state_key.as_deref() == Some(state_key)
                }
            },
            Self::Sync(ev) => match ev {
                PduStub::RoomV1PduStub(ev) => {
                    ev.kind == ev_type && ev.state_key.as_deref() == Some(state_key)
                }
                PduStub::RoomV3PduStub(ev) => {
                    ev.kind == ev_type && ev.state_key.as_deref() == Some(state_key)
                }
            },
        }
    }
}

impl<'de> de::Deserialize<'de> for StateEvent {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: de::Deserializer<'de>,
    {
        let json = Box::<RawJsonValue>::deserialize(deserializer)?;
        let EventDeHelper {
            room_id, unsigned, ..
        } = from_raw_json_value(&json)?;

        // Determine whether the event is a full or sync
        // based on the fields present.
        if room_id.is_some() {
            Ok(match unsigned {
                Some(unsigned) if unsigned.redacted_because.is_some() => {
                    panic!("TODO deal with redacted events")
                }
                _ => StateEvent::Full(Pdu::RoomV1Pdu(from_raw_json_value(&json)?)),
            })
        } else {
            Ok(match unsigned {
                Some(unsigned) if unsigned.redacted_because.is_some() => {
                    panic!("TODO deal with redacted events")
                }
                _ => StateEvent::Sync(from_raw_json_value(&json)?),
            })
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2014-2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// 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.

#![warn(clippy::for_kv_map)]
#![allow(clippy::used_underscore_binding)]

use std::collections::*;
use std::rc::Rc;

fn main() {
    let m: HashMap<u64, u64> = HashMap::new();
    for (_, v) in &m {
        let _v = v;
    }

    let m: Rc<HashMap<u64, u64>> = Rc::new(HashMap::new());
    for (_, v) in &*m {
        let _v = v;
        // Here the `*` is not actually necessary, but the test tests that we don't
        // suggest
        // `in *m.values()` as we used to
    }

    let mut m: HashMap<u64, u64> = HashMap::new();
    for (_, v) in &mut m {
        let _v = v;
    }

    let m: &mut HashMap<u64, u64> = &mut HashMap::new();
    for (_, v) in &mut *m {
        let _v = v;
    }

    let m: HashMap<u64, u64> = HashMap::new();
    let rm = &m;
    for (k, _value) in rm {
        let _k = k;
    }
    test_for_kv_map();
}

fn test_for_kv_map() {
    let m: HashMap<u64, u64> = HashMap::new();

    // No error, _value is actually used
    for (k, _value) in &m {
        let _ = _value;
        let _k = k;
    }
}
<commit_msg>Add ui/for_kv_map test for false positive in #1279<commit_after>// Copyright 2014-2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// 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.

#![warn(clippy::for_kv_map)]
#![allow(clippy::used_underscore_binding)]

use std::collections::*;
use std::rc::Rc;

fn main() {
    let m: HashMap<u64, u64> = HashMap::new();
    for (_, v) in &m {
        let _v = v;
    }

    let m: Rc<HashMap<u64, u64>> = Rc::new(HashMap::new());
    for (_, v) in &*m {
        let _v = v;
        // Here the `*` is not actually necessary, but the test tests that we don't
        // suggest
        // `in *m.values()` as we used to
    }

    let mut m: HashMap<u64, u64> = HashMap::new();
    for (_, v) in &mut m {
        let _v = v;
    }

    let m: &mut HashMap<u64, u64> = &mut HashMap::new();
    for (_, v) in &mut *m {
        let _v = v;
    }

    let m: HashMap<u64, u64> = HashMap::new();
    let rm = &m;
    for (k, _value) in rm {
        let _k = k;
    }

    // The following should not produce warnings.

    let m: HashMap<u64, u64> = HashMap::new();
    // No error, _value is actually used
    for (k, _value) in &m {
        let _ = _value;
        let _k = k;
    }

    let m: HashMap<u64, String> = Default::default();
    for (_, v) in m {
        let _v = v;
    }
}
<|endoftext|>


<commit_before>use std::iter::Peekable;
use std::io;

pub struct LineColIterator<Iter: Iterator<Item=io::Result<u8>>> {
    rdr: Peekable<Iter>,
    line: usize,
    col: usize,
}

impl<Iter: Iterator<Item=io::Result<u8>>> LineColIterator<Iter> {
    pub fn new(iter: Iter) -> LineColIterator<Iter> {
        LineColIterator {
            line: 1,
            col: 0,
            rdr: iter.peekable(),
        }
    }
    fn peek(&mut self) -> Option<u8> {
        match self.rdr.peek() {
            None => None,
            Some(&Ok(c)) => Some(c),
            Some(&Err(_)) => None,
        }
    }
    pub fn line(&self) -> usize { self.line }
    pub fn col(&self) -> usize { self.col }
}

impl<Iter: Iterator<Item=io::Result<u8>>> Iterator for LineColIterator<Iter> {
    type Item = io::Result<u8>;
    fn next(&mut self) -> Option<io::Result<u8>> {
        match self.rdr.next() {
            None => None,
            Some(Ok(b'\n')) => {
                self.line += 1;
                self.col = 0;
                Some(Ok(b'\n'))
            },
            Some(Ok(c)) => {
                self.col += 1;
                Some(Ok(c))
            },
            Some(Err(e)) => Some(Err(e)),
        }
    }
}
<commit_msg>LineColIterator doesn't need to be peekable.<commit_after>use std::io;

pub struct LineColIterator<Iter: Iterator<Item=io::Result<u8>>> {
    rdr: Iter,
    line: usize,
    col: usize,
}

impl<Iter: Iterator<Item=io::Result<u8>>> LineColIterator<Iter> {
    pub fn new(iter: Iter) -> LineColIterator<Iter> {
        LineColIterator {
            line: 1,
            col: 0,
            rdr: iter,
        }
    }
    pub fn line(&self) -> usize { self.line }
    pub fn col(&self) -> usize { self.col }
}

impl<Iter: Iterator<Item=io::Result<u8>>> Iterator for LineColIterator<Iter> {
    type Item = io::Result<u8>;
    fn next(&mut self) -> Option<io::Result<u8>> {
        match self.rdr.next() {
            None => None,
            Some(Ok(b'\n')) => {
                self.line += 1;
                self.col = 0;
                Some(Ok(b'\n'))
            },
            Some(Ok(c)) => {
                self.col += 1;
                Some(Ok(c))
            },
            Some(Err(e)) => Some(Err(e)),
        }
    }
}
<|endoftext|>


<commit_before>//! An iterator yields a sequence of key/value pairs from a source.

use std::fmt;
use std::iter;
use std::marker::PhantomData;
use std::mem;
use std::os::raw::c_void;
use std::slice;

use rocks_sys as ll;

use crate::to_raw::FromRaw;
use crate::{Error, Result};

/// An iterator yields a sequence of key/value pairs from a source.
///
/// Multiple threads can invoke const methods on an Iterator without
/// external synchronization, but if any of the threads may call a
/// non-const method, all threads accessing the same Iterator must use
/// external synchronization.
pub struct Iterator<'a> {
    raw: *mut ll::rocks_iterator_t,
    initial: bool,
    _marker: PhantomData<&'a ()>,
}

unsafe impl<'a> Send for Iterator<'a> {}
// unsafe impl Sync for Iterator {}

impl<'a> fmt::Debug for Iterator<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Iterator(")?;
        if self.is_valid() {
            write!(f, "key={:?})", String::from_utf8_lossy(self.key()))
        } else {
            write!(f, "INVALID)")
        }
    }
}

impl<'a> Drop for Iterator<'a> {
    fn drop(&mut self) {
        unsafe {
            ll::rocks_iter_destroy(self.raw);
        }
    }
}

impl<'a> FromRaw<ll::rocks_iterator_t> for Iterator<'a> {
    unsafe fn from_ll(raw: *mut ll::rocks_iterator_t) -> Self {
        let mut it = Iterator {
            raw: raw,
            initial: true,
            _marker: PhantomData,
        };
        if !it.is_valid() {
            it.seek_to_first();
        }
        // FIXME: test_list_cfs failes
        /*
        debug_assert_eq!(
            it.get_property("rocksdb.iterator.is-key-pinned").unwrap_or_default(),
            "1",
            "key is not pinned!"
        );
        */
        it
    }
}

impl<'a> Iterator<'a> {
    /// An iterator is either positioned at a key/value pair, or
    /// not valid.  This method returns true iff the iterator is valid.
    pub fn is_valid(&self) -> bool {
        unsafe { ll::rocks_iter_valid(self.raw) != 0 }
    }

    /// Position at the first key in the source.  The iterator `is_valid()`
    /// after this call iff the source is not empty.
    pub fn seek_to_first(&mut self) {
        unsafe {
            ll::rocks_iter_seek_to_first(self.raw);
        }
    }

    /// Position at the last key in the source.  The iterator
    /// `is_valid()` after this call iff the source is not empty.
    pub fn seek_to_last(&mut self) {
        unsafe {
            ll::rocks_iter_seek_to_last(self.raw);
        }
    }

    /// Position at the first key in the source that at or past target
    /// The iterator `is_valid()` after this call iff the source contains
    /// an entry that comes at or past target.
    pub fn seek(&mut self, target: &[u8]) {
        unsafe {
            ll::rocks_iter_seek(self.raw, target.as_ptr() as _, target.len());
        }
    }

    /// Position at the last key in the source that at or before target
    /// The iterator `is_valid()` after this call iff the source contains
    /// an entry that comes at or before target.
    pub fn seek_for_prev(&mut self, target: &[u8]) {
        unsafe {
            ll::rocks_iter_seek_for_prev(self.raw, target.as_ptr() as _, target.len());
        }
    }

    /// Moves to the next entry in the source.  After this call, `is_valid()` is
    /// true iff the iterator was not positioned at the last entry in the source.
    ///
    /// REQUIRES: `is_valid()`
    pub fn next(&mut self) {
        unsafe {
            ll::rocks_iter_next(self.raw);
        }
    }

    /// Moves to the previous entry in the source.  After this call, `is_valid()` is
    /// true iff the iterator was not positioned at the first entry in source.
    ///
    /// REQUIRES: `is_valid()`
    pub fn prev(&mut self) {
        unsafe {
            ll::rocks_iter_prev(self.raw);
        }
    }

    /// Return the key for the current entry.  The underlying storage for
    /// the returned slice is valid only until the next modification of
    /// the iterator.
    ///
    /// REQUIRES: `is_valid()`
    pub fn key(&self) -> &'a [u8] {
        unsafe {
            let mut len = 0;
            let ptr = ll::rocks_iter_key(self.raw, &mut len);
            slice::from_raw_parts(ptr as _, len)
        }
    }

    /// Return the value for the current entry.  The underlying storage for
    /// the returned slice is valid only until the next modification of
    /// the iterator.
    ///
    /// REQUIRES: `!AtEnd() && !AtStart()`
    pub fn value(&self) -> &'a [u8] {
        unsafe {
            let mut len = 0;
            let ptr = ll::rocks_iter_value(self.raw, &mut len);
            slice::from_raw_parts(ptr as _, len)
        }
    }

    /// If an error has occurred, return it.  Else return an ok status.
    /// If non-blocking IO is requested and this operation cannot be
    /// satisfied without doing some IO, then this returns `Error::Incomplete()`.
    pub fn status(&self) -> Result<()> {
        unsafe {
            let mut status = mem::zeroed();
            ll::rocks_iter_get_status(self.raw, &mut status);
            Error::from_ll(status)
        }
    }

    /// Property `"rocksdb.iterator.is-key-pinned"`:
    ///
    /// - If returning "1", this means that the Slice returned by key() is valid as long as the
    ///   iterator is not deleted.
    /// - It is guaranteed to always return "1" if
    ///   - Iterator created with `ReadOptions::pin_data = true`
    ///   - DB tables were created with `BlockBasedTableOptions::use_delta_encoding = false`.
    ///
    /// Property `"rocksdb.iterator.super-version-number"`:
    ///
    /// - LSM version used by the iterator. The same format as DB Property
    /// - `kCurrentSuperVersionNumber`. See its comment for more information.
    pub fn get_property(&self, property: &str) -> Result<String> {
        unsafe {
            let mut ret = String::new();
            let mut status = mem::zeroed();
            ll::rocks_iter_get_property(
                self.raw,
                property.as_bytes().as_ptr() as *const _,
                property.len(),
                &mut ret as *mut String as *mut c_void,
                &mut status,
            );
            // FIXME: rocksdb return error string in get_property
            Error::from_ll(status).map(|_| ret)
        }
    }

    /// Consume and make a reversed rustic style iterator.
    pub fn rev(mut self) -> IntoRevIter<'a> {
        self.seek_to_last();
        IntoRevIter { inner: self }
    }

    /// An iterator visiting all keys in current order.
    pub fn keys(self) -> Keys<'a> {
        Keys { inner: self }
    }

    /// An iterator visiting all values in current order.
    pub fn values(self) -> Values<'a> {
        Values { inner: self }
    }
}

impl<'a> iter::Iterator for Iterator<'a> {
    type Item = (&'a [u8], &'a [u8]);

    fn next(&mut self) -> Option<Self::Item> {
        if self.initial {
            self.initial = false;
        } else {
            self.next();
        }
        if self.is_valid() {
            let k = self.key();
            let v = self.value();
            Some((k, v))
        } else {
            None
        }
    }
}

/// Wraps reverse iteration into a rust-style Iterator
pub struct IntoRevIter<'a> {
    inner: Iterator<'a>,
}

impl<'a> IntoRevIter<'a> {
    pub fn into_inner(self) -> Iterator<'a> {
        self.inner
    }

    pub fn seek(&mut self, target: &[u8]) {
        self.inner.seek(target)
    }

    pub fn seek_for_prev(&mut self, target: &[u8]) {
        self.inner.seek_for_prev(target)
    }

    pub fn keys(self) -> RevKeys<'a> {
        RevKeys { inner: self.inner }
    }

    pub fn values(self) -> RevValues<'a> {
        RevValues { inner: self.inner }
    }
}

impl<'a> iter::Iterator for IntoRevIter<'a> {
    type Item = (&'a [u8], &'a [u8]);

    fn next(&mut self) -> Option<Self::Item> {
        if self.inner.initial {
            self.inner.initial = false;
        } else {
            self.inner.prev();
        }
        if self.inner.is_valid() {
            let k = self.inner.key();
            let v = self.inner.value();
            Some((k, v))
        } else {
            None
        }
    }
}

pub struct Keys<'a> {
    inner: Iterator<'a>,
}

impl<'a> Keys<'a> {
    pub fn rev(self) -> RevKeys<'a> {
        RevKeys { inner: self.inner }
    }

    pub fn seek(&mut self, target: &[u8]) {
        self.inner.seek(target)
    }

    pub fn seek_for_prev(&mut self, target: &[u8]) {
        self.inner.seek_for_prev(target)
    }
}

impl<'a> iter::Iterator for Keys<'a> {
    type Item = &'a [u8];

    fn next(&mut self) -> Option<Self::Item> {
        if self.inner.is_valid() {
            let k = self.inner.key();
            self.inner.next();
            Some(k)
        } else {
            None
        }
    }
}

pub struct RevKeys<'a> {
    inner: Iterator<'a>,
}

impl<'a> iter::Iterator for RevKeys<'a> {
    type Item = &'a [u8];

    fn next(&mut self) -> Option<Self::Item> {
        if self.inner.is_valid() {
            let k = self.inner.key();
            self.inner.prev();
            Some(k)
        } else {
            None
        }
    }
}

pub struct Values<'a> {
    inner: Iterator<'a>,
}

impl<'a> Values<'a> {
    // FIXME: is this useless?
    pub fn rev(self) -> RevValues<'a> {
        RevValues { inner: self.inner }
    }
}

impl<'a> iter::Iterator for Values<'a> {
    type Item = &'a [u8];

    fn next(&mut self) -> Option<Self::Item> {
        if self.inner.is_valid() {
            let v = self.inner.value();
            self.inner.next();
            Some(v)
        } else {
            None
        }
    }
}

pub struct RevValues<'a> {
    inner: Iterator<'a>,
}

impl<'a> iter::Iterator for RevValues<'a> {
    type Item = &'a [u8];

    fn next(&mut self) -> Option<Self::Item> {
        if self.inner.is_valid() {
            let v = self.inner.value();
            self.inner.prev();
            Some(v)
        } else {
            None
        }
    }
}

#[cfg(test)]
mod tests {
    use super::super::rocksdb::*;

    #[test]
    fn iterator() {
        use tempdir::TempDir;
        let tmp_dir = TempDir::new_in(".", "rocks").unwrap();
        let opt = Options::default()
            .map_db_options(|db| db.create_if_missing(true))
            .map_cf_options(|cf| {
                cf.table_factory_block_based(BlockBasedTableOptions::default().use_delta_encoding(false))
            });
        let db = DB::open(opt, tmp_dir.path()).unwrap();
        let mut batch = WriteBatch::new();

        batch
            .put(b"key1", b"BYasdf1CQ")
            .put(b"key2", b"BYasdf1CQ")
            .put(b"key3", b"BYasdf1CQ")
            .put(b"key4", b"BY1dfsgCQ")
            .put(b"key5", b"BY1ghCQ")
            .put(b"key0", b"BYwertw1CQ")
            .put(b"key_", b"BY1C234Q")
            .put(b"key4", b"BY1xcvbCQ")
            .put(b"key5", b"BY1gjhkjCQ")
            .put(b"key1", b"BY1CyuitQ")
            .put(b"key8", b"BY1CvbncvQ")
            .put(b"key4", b"BY1CsafQ")
            .put(b"name", b"BH1XUwqrW")
            .put(b"site", b"githuzxcvb");

        let ret = db.write(&WriteOptions::default(), &batch);
        assert!(ret.is_ok());

        assert!(db.compact_range(&Default::default(), ..).is_ok());

        {
            for (k, v) in db.new_iterator(&ReadOptions::default().pin_data(true)).into_iter() {
                println!("> {:?} => {:?}", String::from_utf8_lossy(k), String::from_utf8_lossy(v));
            }
        }

        assert!(ret.is_ok());
        {
            // must pin_data
            let kvs = db
                .new_iterator(&ReadOptions::default().pin_data(true))
                .into_iter()
                .map(|(k, v)| (String::from_utf8_lossy(k), String::from_utf8_lossy(v)))
                .collect::<Vec<_>>();
            println!("got kv => {:?}", kvs);
        }

        let mut it = db.new_iterator(&ReadOptions::default().pin_data(true));

        assert_eq!(it.is_valid(), true);
        println!("it => {:?}", it);

        it.seek_to_first();
        assert_eq!(it.get_property("rocksdb.iterator.is-key-pinned"), Ok("1".to_string()));

        println!(
            "got => {:?}",
            it.get_property("rocksdb.iterator.super-version-number").unwrap()
        );

        assert_eq!(it.is_valid(), true);
        println!("it => {:?}", it);
        it.next();
        println!("it => {:?}", it);
        it.seek_to_last();
        println!("it => {:?}", it);
        it.next();
        println!("it => {:?}", it);
    }

    #[test]
    fn reversed_iterator() {
        use tempdir::TempDir;
        let tmp_dir = TempDir::new_in(".", "rocks").unwrap();
        let opt = Options::default().map_db_options(|db| db.create_if_missing(true));
        let db = DB::open(opt, tmp_dir.path()).unwrap();

        let mut batch = WriteBatch::new();
        batch
            .put(b"k1", b"BYasdf1CQ")
            .put(b"k8", b"BY1C234Q")
            .put(b"k4", b"BY1dfsgCQ")
            .put(b"k2", b"BYasdf1CQ")
            .put(b"k3", b"BYasdf1CQ")
            .put(b"k6", b"BYwertw1CQ")
            .put(b"k5", b"BY1ghCQ")
            .put(b"k9", b"BY1xcvbCQ");

        let ret = db.write(&WriteOptions::default(), &batch);
        assert!(ret.is_ok());

        let keys: Vec<_> = db
            .new_iterator(&ReadOptions::default().pin_data(true))
            .rev()
            .keys()
            .map(|k| String::from_utf8_lossy(k).to_owned().to_string())
            .collect();
        assert_eq!(keys, vec!["k9", "k8", "k6", "k5", "k4", "k3", "k2", "k1"]);
    }
}
<commit_msg>fix(iterator): wrong order of calling next() for .keys() .values()<commit_after>//! An iterator yields a sequence of key/value pairs from a source.

use std::fmt;
use std::iter;
use std::marker::PhantomData;
use std::mem;
use std::os::raw::c_void;
use std::slice;

use rocks_sys as ll;

use crate::to_raw::FromRaw;
use crate::{Error, Result};

/// An iterator yields a sequence of key/value pairs from a source.
///
/// Multiple threads can invoke const methods on an Iterator without
/// external synchronization, but if any of the threads may call a
/// non-const method, all threads accessing the same Iterator must use
/// external synchronization.
pub struct Iterator<'a> {
    raw: *mut ll::rocks_iterator_t,
    initial: bool,
    _marker: PhantomData<&'a ()>,
}

unsafe impl<'a> Send for Iterator<'a> {}
// unsafe impl Sync for Iterator {}

impl<'a> fmt::Debug for Iterator<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Iterator(")?;
        if self.is_valid() {
            write!(f, "key={:?})", String::from_utf8_lossy(self.key()))
        } else {
            write!(f, "INVALID)")
        }
    }
}

impl<'a> Drop for Iterator<'a> {
    fn drop(&mut self) {
        unsafe {
            ll::rocks_iter_destroy(self.raw);
        }
    }
}

impl<'a> FromRaw<ll::rocks_iterator_t> for Iterator<'a> {
    unsafe fn from_ll(raw: *mut ll::rocks_iterator_t) -> Self {
        let mut it = Iterator {
            raw: raw,
            initial: true,
            _marker: PhantomData,
        };
        if !it.is_valid() {
            it.seek_to_first();
        }
        // FIXME: test_list_cfs failes
        /*
        debug_assert_eq!(
            it.get_property("rocksdb.iterator.is-key-pinned").unwrap_or_default(),
            "1",
            "key is not pinned!"
        );
        */
        it
    }
}

impl<'a> Iterator<'a> {
    /// An iterator is either positioned at a key/value pair, or
    /// not valid.  This method returns true iff the iterator is valid.
    pub fn is_valid(&self) -> bool {
        unsafe { ll::rocks_iter_valid(self.raw) != 0 }
    }

    /// Position at the first key in the source.  The iterator `is_valid()`
    /// after this call iff the source is not empty.
    pub fn seek_to_first(&mut self) {
        unsafe {
            ll::rocks_iter_seek_to_first(self.raw);
        }
    }

    /// Position at the last key in the source.  The iterator
    /// `is_valid()` after this call iff the source is not empty.
    pub fn seek_to_last(&mut self) {
        unsafe {
            ll::rocks_iter_seek_to_last(self.raw);
        }
    }

    /// Position at the first key in the source that at or past target
    /// The iterator `is_valid()` after this call iff the source contains
    /// an entry that comes at or past target.
    pub fn seek(&mut self, target: &[u8]) {
        unsafe {
            ll::rocks_iter_seek(self.raw, target.as_ptr() as _, target.len());
        }
    }

    /// Position at the last key in the source that at or before target
    /// The iterator `is_valid()` after this call iff the source contains
    /// an entry that comes at or before target.
    pub fn seek_for_prev(&mut self, target: &[u8]) {
        unsafe {
            ll::rocks_iter_seek_for_prev(self.raw, target.as_ptr() as _, target.len());
        }
    }

    /// Moves to the next entry in the source.  After this call, `is_valid()` is
    /// true iff the iterator was not positioned at the last entry in the source.
    ///
    /// REQUIRES: `is_valid()`
    pub fn next(&mut self) {
        unsafe {
            ll::rocks_iter_next(self.raw);
        }
    }

    /// Moves to the previous entry in the source.  After this call, `is_valid()` is
    /// true iff the iterator was not positioned at the first entry in source.
    ///
    /// REQUIRES: `is_valid()`
    pub fn prev(&mut self) {
        unsafe {
            ll::rocks_iter_prev(self.raw);
        }
    }

    /// Return the key for the current entry.  The underlying storage for
    /// the returned slice is valid only until the next modification of
    /// the iterator.
    ///
    /// REQUIRES: `is_valid()`
    pub fn key(&self) -> &'a [u8] {
        unsafe {
            let mut len = 0;
            let ptr = ll::rocks_iter_key(self.raw, &mut len);
            slice::from_raw_parts(ptr as _, len)
        }
    }

    /// Return the value for the current entry.  The underlying storage for
    /// the returned slice is valid only until the next modification of
    /// the iterator.
    ///
    /// REQUIRES: `!AtEnd() && !AtStart()`
    pub fn value(&self) -> &'a [u8] {
        unsafe {
            let mut len = 0;
            let ptr = ll::rocks_iter_value(self.raw, &mut len);
            slice::from_raw_parts(ptr as _, len)
        }
    }

    /// If an error has occurred, return it.  Else return an ok status.
    /// If non-blocking IO is requested and this operation cannot be
    /// satisfied without doing some IO, then this returns `Error::Incomplete()`.
    pub fn status(&self) -> Result<()> {
        unsafe {
            let mut status = mem::zeroed();
            ll::rocks_iter_get_status(self.raw, &mut status);
            Error::from_ll(status)
        }
    }

    /// Property `"rocksdb.iterator.is-key-pinned"`:
    ///
    /// - If returning "1", this means that the Slice returned by key() is valid as long as the
    ///   iterator is not deleted.
    /// - It is guaranteed to always return "1" if
    ///   - Iterator created with `ReadOptions::pin_data = true`
    ///   - DB tables were created with `BlockBasedTableOptions::use_delta_encoding = false`.
    ///
    /// Property `"rocksdb.iterator.super-version-number"`:
    ///
    /// - LSM version used by the iterator. The same format as DB Property
    /// - `kCurrentSuperVersionNumber`. See its comment for more information.
    pub fn get_property(&self, property: &str) -> Result<String> {
        unsafe {
            let mut ret = String::new();
            let mut status = mem::zeroed();
            ll::rocks_iter_get_property(
                self.raw,
                property.as_bytes().as_ptr() as *const _,
                property.len(),
                &mut ret as *mut String as *mut c_void,
                &mut status,
            );
            // FIXME: rocksdb return error string in get_property
            Error::from_ll(status).map(|_| ret)
        }
    }

    /// Consume and make a reversed rustic style iterator.
    pub fn rev(mut self) -> IntoRevIter<'a> {
        self.seek_to_last();
        IntoRevIter { inner: self }
    }

    /// An iterator visiting all keys in current order.
    pub fn keys(self) -> Keys<'a> {
        Keys { inner: self }
    }

    /// An iterator visiting all values in current order.
    pub fn values(self) -> Values<'a> {
        Values { inner: self }
    }
}

impl<'a> iter::Iterator for Iterator<'a> {
    type Item = (&'a [u8], &'a [u8]);

    fn next(&mut self) -> Option<Self::Item> {
        if self.initial {
            self.initial = false;
        } else {
            self.next();
        }
        if self.is_valid() {
            Some((self.key(), self.value()))
        } else {
            None
        }
    }
}

/// Wraps reverse iteration into a rust-style Iterator
pub struct IntoRevIter<'a> {
    inner: Iterator<'a>,
}

impl<'a> IntoRevIter<'a> {
    pub fn into_inner(self) -> Iterator<'a> {
        self.inner
    }

    pub fn seek(&mut self, target: &[u8]) {
        self.inner.seek(target)
    }

    pub fn seek_for_prev(&mut self, target: &[u8]) {
        self.inner.seek_for_prev(target)
    }

    pub fn keys(self) -> RevKeys<'a> {
        RevKeys { inner: self.inner }
    }

    pub fn values(self) -> RevValues<'a> {
        RevValues { inner: self.inner }
    }
}

impl<'a> iter::Iterator for IntoRevIter<'a> {
    type Item = (&'a [u8], &'a [u8]);

    fn next(&mut self) -> Option<Self::Item> {
        if self.inner.initial {
            self.inner.initial = false;
        } else {
            self.inner.prev();
        }
        if self.inner.is_valid() {
            Some((self.inner.key(), self.inner.value()))
        } else {
            None
        }
    }
}

pub struct Keys<'a> {
    inner: Iterator<'a>,
}

impl<'a> Keys<'a> {
    pub fn rev(self) -> RevKeys<'a> {
        RevKeys { inner: self.inner }
    }

    pub fn seek(&mut self, target: &[u8]) {
        self.inner.seek(target)
    }

    pub fn seek_for_prev(&mut self, target: &[u8]) {
        self.inner.seek_for_prev(target)
    }
}

impl<'a> iter::Iterator for Keys<'a> {
    type Item = &'a [u8];

    fn next(&mut self) -> Option<Self::Item> {
        if self.inner.initial {
            self.inner.initial = false;
        } else {
            self.inner.next();
        }
        if self.inner.is_valid() {
            self.inner.next();
            Some(self.inner.key())
        } else {
            None
        }
    }
}

pub struct RevKeys<'a> {
    inner: Iterator<'a>,
}

impl<'a> iter::Iterator for RevKeys<'a> {
    type Item = &'a [u8];

    fn next(&mut self) -> Option<Self::Item> {
        if self.inner.initial {
            self.inner.initial = false;
        } else {
            self.inner.prev();
        }
        if self.inner.is_valid() {
            Some(self.inner.key())
        } else {
            None
        }
    }
}

pub struct Values<'a> {
    inner: Iterator<'a>,
}

impl<'a> Values<'a> {
    // FIXME: is this useless?
    pub fn rev(self) -> RevValues<'a> {
        RevValues { inner: self.inner }
    }
}

impl<'a> iter::Iterator for Values<'a> {
    type Item = &'a [u8];

    fn next(&mut self) -> Option<Self::Item> {
        if self.inner.initial {
            self.inner.initial = false;
        } else {
            self.inner.next();
        }
        if self.inner.is_valid() {
            Some(self.inner.value())
        } else {
            None
        }
    }
}

pub struct RevValues<'a> {
    inner: Iterator<'a>,
}

impl<'a> iter::Iterator for RevValues<'a> {
    type Item = &'a [u8];

    fn next(&mut self) -> Option<Self::Item> {
        if self.inner.initial {
            self.inner.initial = false;
        } else {
            self.inner.prev();
        }
        if self.inner.is_valid() {
            Some(self.inner.value())
        } else {
            None
        }
    }
}

#[cfg(test)]
mod tests {
    use super::super::rocksdb::*;

    #[test]
    fn iterator() {
        use tempdir::TempDir;
        let tmp_dir = TempDir::new_in(".", "rocks").unwrap();
        let opt = Options::default()
            .map_db_options(|db| db.create_if_missing(true))
            .map_cf_options(|cf| {
                cf.table_factory_block_based(BlockBasedTableOptions::default().use_delta_encoding(false))
            });
        let db = DB::open(opt, tmp_dir.path()).unwrap();
        let mut batch = WriteBatch::new();

        batch
            .put(b"key1", b"BYasdf1CQ")
            .put(b"key2", b"BYasdf1CQ")
            .put(b"key3", b"BYasdf1CQ")
            .put(b"key4", b"BY1dfsgCQ")
            .put(b"key5", b"BY1ghCQ")
            .put(b"key0", b"BYwertw1CQ")
            .put(b"key_", b"BY1C234Q")
            .put(b"key4", b"BY1xcvbCQ")
            .put(b"key5", b"BY1gjhkjCQ")
            .put(b"key1", b"BY1CyuitQ")
            .put(b"key8", b"BY1CvbncvQ")
            .put(b"key4", b"BY1CsafQ")
            .put(b"name", b"BH1XUwqrW")
            .put(b"site", b"githuzxcvb");

        let ret = db.write(&WriteOptions::default(), &batch);
        assert!(ret.is_ok());

        assert!(db.compact_range(&Default::default(), ..).is_ok());

        {
            for (k, v) in db.new_iterator(&ReadOptions::default().pin_data(true)).into_iter() {
                println!("> {:?} => {:?}", String::from_utf8_lossy(k), String::from_utf8_lossy(v));
            }
        }

        assert!(ret.is_ok());
        {
            // must pin_data
            let kvs = db
                .new_iterator(&ReadOptions::default().pin_data(true))
                .into_iter()
                .map(|(k, v)| (String::from_utf8_lossy(k), String::from_utf8_lossy(v)))
                .collect::<Vec<_>>();
            println!("got kv => {:?}", kvs);
        }

        let mut it = db.new_iterator(&ReadOptions::default().pin_data(true));

        assert_eq!(it.is_valid(), true);
        println!("it => {:?}", it);

        it.seek_to_first();
        assert_eq!(it.get_property("rocksdb.iterator.is-key-pinned"), Ok("1".to_string()));

        println!(
            "got => {:?}",
            it.get_property("rocksdb.iterator.super-version-number").unwrap()
        );

        assert_eq!(it.is_valid(), true);
        println!("it => {:?}", it);
        it.next();
        println!("it => {:?}", it);
        it.seek_to_last();
        println!("it => {:?}", it);
        it.next();
        println!("it => {:?}", it);
    }

    #[test]
    fn reversed_iterator() {
        use tempdir::TempDir;
        let tmp_dir = TempDir::new_in(".", "rocks").unwrap();
        let opt = Options::default().map_db_options(|db| db.create_if_missing(true));
        let db = DB::open(opt, tmp_dir.path()).unwrap();

        let mut batch = WriteBatch::new();
        batch
            .put(b"k1", b"BYasdf1CQ")
            .put(b"k8", b"BY1C234Q")
            .put(b"k4", b"BY1dfsgCQ")
            .put(b"k2", b"BYasdf1CQ")
            .put(b"k3", b"BYasdf1CQ")
            .put(b"k6", b"BYwertw1CQ")
            .put(b"k5", b"BY1ghCQ")
            .put(b"k9", b"BY1xcvbCQ");

        let ret = db.write(&WriteOptions::default(), &batch);
        assert!(ret.is_ok());

        let keys: Vec<_> = db
            .new_iterator(&ReadOptions::default().pin_data(true))
            .rev()
            .keys()
            .map(|k| String::from_utf8_lossy(k).to_owned().to_string())
            .collect();
        assert_eq!(keys, vec!["k9", "k8", "k6", "k5", "k4", "k3", "k2", "k1"]);
    }
}
<|endoftext|>