<commit_before>// Copyright 2015 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Render target components for the PSO macro.

use std::marker::PhantomData;
use gfx_core::{ColorSlot, Resources};
use gfx_core::{format, handle, pso, state, target};
use gfx_core::factory::Typed;
use gfx_core::shade::OutputVar;
use super::{DataLink, DataBind, RawDataSet};

/// Render target component. Typically points to a color-formatted texture.
/// - init: `&str` = name of the target
/// - data: `RenderTargetView<T>`
pub struct RenderTarget<T>(Option<ColorSlot>, PhantomData<T>);
/// Render target component with active blending mode.
/// - init: (`&str`, `ColorMask`, `Blend` = blending state)
/// - data: `RenderTargetView<T>`
pub struct BlendTarget<T>(Option<ColorSlot>, PhantomData<T>);
/// Raw (untyped) render target component with optional blending.
/// - init: (`&str`, `Format`, `ColorMask`, `Option<Blend>`)
/// - data: `RawRenderTargetView`
pub struct RawRenderTarget(Option<ColorSlot>);
/// Depth target component.
/// - init: `Depth` = depth state
/// - data: `DepthStencilView<T>`
pub struct DepthTarget<T>(PhantomData<T>);
/// Stencil target component.
/// - init: `Stencil` = stencil state
/// - data: (`DepthStencilView<T>`, `(front, back)` = stencil reference values)
pub struct StencilTarget<T>(PhantomData<T>);
/// Depth + stencil target component.
/// - init: (`Depth` = depth state, `Stencil` = stencil state)
/// - data: (`DepthStencilView<T>`, `(front, back)` = stencil reference values)
pub struct DepthStencilTarget<T>(PhantomData<T>);
/// Scissor component. Sets up the scissor test for rendering.
/// - init: `()`
/// - data: `Rect` = target area
pub struct Scissor(bool);
/// Blend reference component. Sets up the reference color for blending.
/// - init: `()`
/// - data: `ColorValue`
pub struct BlendRef;

impl<'a, T: format::RenderFormat> DataLink<'a> for RenderTarget<T> {
    type Init = &'a str;
    fn new() -> Self {
        RenderTarget(None, PhantomData)
    }
    fn is_active(&self) -> bool {
        self.0.is_some()
    }
    fn link_output(&mut self, out: &OutputVar, init: &Self::Init) ->
                   Option<Result<pso::ColorTargetDesc, format::Format>> {
        if out.name.is_empty() || &out.name == init {
            self.0 = Some(out.slot);
            let desc = (T::get_format(), state::MASK_ALL.into());
            Some(Ok(desc))
        }else {
            None
        }
    }
}

impl<R: Resources, T> DataBind<R> for RenderTarget<T> {
    type Data = handle::RenderTargetView<R, T>;
    fn bind_to(&self, out: &mut RawDataSet<R>, data: &Self::Data, man: &mut handle::Manager<R>) {
        if let Some(slot) = self.0 {
            out.pixel_targets.add_color(slot, man.ref_rtv(data.raw()), data.raw().get_dimensions());
        }
    }
}

impl<'a, T: format::BlendFormat> DataLink<'a> for BlendTarget<T> {
    type Init = (&'a str, state::ColorMask, state::Blend);
    fn new() -> Self {
        BlendTarget(None, PhantomData)
    }
    fn is_active(&self) -> bool {
        self.0.is_some()
    }
    fn link_output(&mut self, out: &OutputVar, init: &Self::Init) ->
                   Option<Result<pso::ColorTargetDesc, format::Format>> {
        if out.name.is_empty() || &out.name == init.0 {
            self.0 = Some(out.slot);
            let desc = (T::get_format(), pso::ColorInfo {
                mask: init.1,
                color: Some(init.2.color),
                alpha: Some(init.2.alpha),
            });
            Some(Ok(desc))
        }else {
            None
        }
    }
}

impl<R: Resources, T> DataBind<R> for BlendTarget<T> {
    type Data = handle::RenderTargetView<R, T>;
    fn bind_to(&self, out: &mut RawDataSet<R>, data: &Self::Data, man: &mut handle::Manager<R>) {
        if let Some(slot) = self.0 {
            out.pixel_targets.add_color(slot, man.ref_rtv(data.raw()), data.raw().get_dimensions());
        }
    }
}

impl<'a> DataLink<'a> for RawRenderTarget {
    type Init = (&'a str, format::Format, state::ColorMask, Option<state::Blend>);
    fn new() -> Self {
        RawRenderTarget(None)
    }
    fn is_active(&self) -> bool {
        self.0.is_some()
    }
    fn link_output(&mut self, out: &OutputVar, init: &Self::Init) ->
                   Option<Result<pso::ColorTargetDesc, format::Format>> {
        if out.name.is_empty() || &out.name == init.0 {
            self.0 = Some(out.slot);
            let desc = (init.1, pso::ColorInfo {
                mask: init.2,
                color: init.3.map(|b| b.color),
                alpha: init.3.map(|b| b.alpha),
            });
            Some(Ok(desc))
        }else {
            None
        }
    }
}

impl<R: Resources> DataBind<R> for RawRenderTarget {
    type Data = handle::RawRenderTargetView<R>;
    fn bind_to(&self, out: &mut RawDataSet<R>, data: &Self::Data, man: &mut handle::Manager<R>) {
        if let Some(slot) = self.0 {
            out.pixel_targets.add_color(slot, man.ref_rtv(data), data.get_dimensions());
        }
    }
}

impl<'a, T: format::DepthFormat> DataLink<'a> for DepthTarget<T> {
    type Init = state::Depth;
    fn new() -> Self { DepthTarget(PhantomData) }
    fn is_active(&self) -> bool { true }
    fn link_depth_stencil(&mut self, init: &Self::Init) -> Option<pso::DepthStencilDesc> {
        Some((T::get_format().0, (*init).into()))
    }
}

impl<R: Resources, T> DataBind<R> for DepthTarget<T> {
    type Data = handle::DepthStencilView<R, T>;
    fn bind_to(&self, out: &mut RawDataSet<R>, data: &Self::Data, man: &mut handle::Manager<R>) {
        let dsv = data.raw();
        out.pixel_targets.add_depth_stencil(man.ref_dsv(dsv), true, false, dsv.get_dimensions());
    }
}

impl<'a, T: format::StencilFormat> DataLink<'a> for StencilTarget<T> {
    type Init = state::Stencil;
    fn new() -> Self { StencilTarget(PhantomData) }
    fn is_active(&self) -> bool { true }
    fn link_depth_stencil(&mut self, init: &Self::Init) -> Option<pso::DepthStencilDesc> {
        Some((T::get_format().0, (*init).into()))
    }
}

impl<R: Resources, T> DataBind<R> for StencilTarget<T> {
    type Data = (handle::DepthStencilView<R, T>, (target::Stencil, target::Stencil));
    fn bind_to(&self, out: &mut RawDataSet<R>, data: &Self::Data, man: &mut handle::Manager<R>) {
        let dsv = data.0.raw();
        out.pixel_targets.add_depth_stencil(man.ref_dsv(dsv), false, true, dsv.get_dimensions());
        out.ref_values.stencil = data.1;
    }
}

impl<'a, T: format::DepthStencilFormat> DataLink<'a> for DepthStencilTarget<T> {
    type Init = (state::Depth, state::Stencil);
    fn new() -> Self { DepthStencilTarget(PhantomData) }
    fn is_active(&self) -> bool { true }
    fn link_depth_stencil(&mut self, init: &Self::Init) -> Option<pso::DepthStencilDesc> {
        Some((T::get_format().0, (*init).into()))
    }
}

impl<R: Resources, T> DataBind<R> for DepthStencilTarget<T> {
    type Data = (handle::DepthStencilView<R, T>, (target::Stencil, target::Stencil));
    fn bind_to(&self, out: &mut RawDataSet<R>, data: &Self::Data, man: &mut handle::Manager<R>) {
        let dsv = data.0.raw();
        out.pixel_targets.add_depth_stencil(man.ref_dsv(dsv), true, true, dsv.get_dimensions());
        out.ref_values.stencil = data.1;
    }
}

impl<'a> DataLink<'a> for Scissor {
    type Init = ();
    fn new() -> Self { Scissor(false) }
    fn is_active(&self) -> bool { self.0 }
    fn link_scissor(&mut self) -> bool { self.0 = true; true }
}

impl<R: Resources> DataBind<R> for Scissor {
    type Data = target::Rect;
    fn bind_to(&self, out: &mut RawDataSet<R>, data: &Self::Data, _: &mut handle::Manager<R>) {
        out.scissor = *data;
    }
}

impl<'a> DataLink<'a> for BlendRef {
    type Init = ();
    fn new() -> Self { BlendRef }
    fn is_active(&self) -> bool { true }
}

impl<R: Resources> DataBind<R> for BlendRef {
    type Data = target::ColorValue;
    fn bind_to(&self, out: &mut RawDataSet<R>, data: &Self::Data, _: &mut handle::Manager<R>) {
        out.ref_values.blend = *data;
    }
}
<commit_msg>PSO - refactored BlendTarget component implementation<commit_after>// Copyright 2015 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Render target components for the PSO macro.

use std::marker::PhantomData;
use gfx_core::{ColorSlot, Resources};
use gfx_core::{format, handle, pso, state, target};
use gfx_core::factory::Typed;
use gfx_core::shade::OutputVar;
use super::{DataLink, DataBind, RawDataSet};

/// Render target component. Typically points to a color-formatted texture.
/// - init: `&str` = name of the target
/// - data: `RenderTargetView<T>`
pub struct RenderTarget<T>(Option<ColorSlot>, PhantomData<T>);
/// Render target component with active blending mode.
/// - init: (`&str`, `ColorMask`, `Blend` = blending state)
/// - data: `RenderTargetView<T>`
pub struct BlendTarget<T>(RawRenderTarget, PhantomData<T>);
/// Raw (untyped) render target component with optional blending.
/// - init: (`&str`, `Format`, `ColorMask`, `Option<Blend>`)
/// - data: `RawRenderTargetView`
pub struct RawRenderTarget(Option<ColorSlot>);
/// Depth target component.
/// - init: `Depth` = depth state
/// - data: `DepthStencilView<T>`
pub struct DepthTarget<T>(PhantomData<T>);
/// Stencil target component.
/// - init: `Stencil` = stencil state
/// - data: (`DepthStencilView<T>`, `(front, back)` = stencil reference values)
pub struct StencilTarget<T>(PhantomData<T>);
/// Depth + stencil target component.
/// - init: (`Depth` = depth state, `Stencil` = stencil state)
/// - data: (`DepthStencilView<T>`, `(front, back)` = stencil reference values)
pub struct DepthStencilTarget<T>(PhantomData<T>);
/// Scissor component. Sets up the scissor test for rendering.
/// - init: `()`
/// - data: `Rect` = target area
pub struct Scissor(bool);
/// Blend reference component. Sets up the reference color for blending.
/// - init: `()`
/// - data: `ColorValue`
pub struct BlendRef;

impl<'a, T: format::RenderFormat> DataLink<'a> for RenderTarget<T> {
    type Init = &'a str;
    fn new() -> Self {
        RenderTarget(None, PhantomData)
    }
    fn is_active(&self) -> bool {
        self.0.is_some()
    }
    fn link_output(&mut self, out: &OutputVar, init: &Self::Init) ->
                   Option<Result<pso::ColorTargetDesc, format::Format>> {
        if out.name.is_empty() || &out.name == init {
            self.0 = Some(out.slot);
            let desc = (T::get_format(), state::MASK_ALL.into());
            Some(Ok(desc))
        }else {
            None
        }
    }
}

impl<R: Resources, T> DataBind<R> for RenderTarget<T> {
    type Data = handle::RenderTargetView<R, T>;
    fn bind_to(&self, out: &mut RawDataSet<R>, data: &Self::Data, man: &mut handle::Manager<R>) {
        if let Some(slot) = self.0 {
            out.pixel_targets.add_color(slot, man.ref_rtv(data.raw()), data.raw().get_dimensions());
        }
    }
}

impl<'a, T: format::BlendFormat> DataLink<'a> for BlendTarget<T> {
    type Init = (&'a str, state::ColorMask, state::Blend);
    fn new() -> Self {
        BlendTarget(RawRenderTarget(None), PhantomData)
    }
    fn is_active(&self) -> bool {
        self.0.is_active()
    }
    fn link_output(&mut self, out: &OutputVar, init: &Self::Init) ->
                   Option<Result<pso::ColorTargetDesc, format::Format>> {
        self.0.link_output(out, &(init.0, T::get_format(), init.1, Some(init.2)))
    }
}

impl<R: Resources, T> DataBind<R> for BlendTarget<T> {
    type Data = handle::RenderTargetView<R, T>;
    fn bind_to(&self, out: &mut RawDataSet<R>, data: &Self::Data, man: &mut handle::Manager<R>) {
        self.0.bind_to(out, data.raw(), man)
    }
}

impl<'a> DataLink<'a> for RawRenderTarget {
    type Init = (&'a str, format::Format, state::ColorMask, Option<state::Blend>);
    fn new() -> Self {
        RawRenderTarget(None)
    }
    fn is_active(&self) -> bool {
        self.0.is_some()
    }
    fn link_output(&mut self, out: &OutputVar, init: &Self::Init) ->
                   Option<Result<pso::ColorTargetDesc, format::Format>> {
        if out.name.is_empty() || &out.name == init.0 {
            self.0 = Some(out.slot);
            let desc = (init.1, pso::ColorInfo {
                mask: init.2,
                color: init.3.map(|b| b.color),
                alpha: init.3.map(|b| b.alpha),
            });
            Some(Ok(desc))
        }else {
            None
        }
    }
}

impl<R: Resources> DataBind<R> for RawRenderTarget {
    type Data = handle::RawRenderTargetView<R>;
    fn bind_to(&self, out: &mut RawDataSet<R>, data: &Self::Data, man: &mut handle::Manager<R>) {
        if let Some(slot) = self.0 {
            out.pixel_targets.add_color(slot, man.ref_rtv(data), data.get_dimensions());
        }
    }
}

impl<'a, T: format::DepthFormat> DataLink<'a> for DepthTarget<T> {
    type Init = state::Depth;
    fn new() -> Self { DepthTarget(PhantomData) }
    fn is_active(&self) -> bool { true }
    fn link_depth_stencil(&mut self, init: &Self::Init) -> Option<pso::DepthStencilDesc> {
        Some((T::get_format().0, (*init).into()))
    }
}

impl<R: Resources, T> DataBind<R> for DepthTarget<T> {
    type Data = handle::DepthStencilView<R, T>;
    fn bind_to(&self, out: &mut RawDataSet<R>, data: &Self::Data, man: &mut handle::Manager<R>) {
        let dsv = data.raw();
        out.pixel_targets.add_depth_stencil(man.ref_dsv(dsv), true, false, dsv.get_dimensions());
    }
}

impl<'a, T: format::StencilFormat> DataLink<'a> for StencilTarget<T> {
    type Init = state::Stencil;
    fn new() -> Self { StencilTarget(PhantomData) }
    fn is_active(&self) -> bool { true }
    fn link_depth_stencil(&mut self, init: &Self::Init) -> Option<pso::DepthStencilDesc> {
        Some((T::get_format().0, (*init).into()))
    }
}

impl<R: Resources, T> DataBind<R> for StencilTarget<T> {
    type Data = (handle::DepthStencilView<R, T>, (target::Stencil, target::Stencil));
    fn bind_to(&self, out: &mut RawDataSet<R>, data: &Self::Data, man: &mut handle::Manager<R>) {
        let dsv = data.0.raw();
        out.pixel_targets.add_depth_stencil(man.ref_dsv(dsv), false, true, dsv.get_dimensions());
        out.ref_values.stencil = data.1;
    }
}

impl<'a, T: format::DepthStencilFormat> DataLink<'a> for DepthStencilTarget<T> {
    type Init = (state::Depth, state::Stencil);
    fn new() -> Self { DepthStencilTarget(PhantomData) }
    fn is_active(&self) -> bool { true }
    fn link_depth_stencil(&mut self, init: &Self::Init) -> Option<pso::DepthStencilDesc> {
        Some((T::get_format().0, (*init).into()))
    }
}

impl<R: Resources, T> DataBind<R> for DepthStencilTarget<T> {
    type Data = (handle::DepthStencilView<R, T>, (target::Stencil, target::Stencil));
    fn bind_to(&self, out: &mut RawDataSet<R>, data: &Self::Data, man: &mut handle::Manager<R>) {
        let dsv = data.0.raw();
        out.pixel_targets.add_depth_stencil(man.ref_dsv(dsv), true, true, dsv.get_dimensions());
        out.ref_values.stencil = data.1;
    }
}

impl<'a> DataLink<'a> for Scissor {
    type Init = ();
    fn new() -> Self { Scissor(false) }
    fn is_active(&self) -> bool { self.0 }
    fn link_scissor(&mut self) -> bool { self.0 = true; true }
}

impl<R: Resources> DataBind<R> for Scissor {
    type Data = target::Rect;
    fn bind_to(&self, out: &mut RawDataSet<R>, data: &Self::Data, _: &mut handle::Manager<R>) {
        out.scissor = *data;
    }
}

impl<'a> DataLink<'a> for BlendRef {
    type Init = ();
    fn new() -> Self { BlendRef }
    fn is_active(&self) -> bool { true }
}

impl<R: Resources> DataBind<R> for BlendRef {
    type Data = target::ColorValue;
    fn bind_to(&self, out: &mut RawDataSet<R>, data: &Self::Data, _: &mut handle::Manager<R>) {
        out.ref_values.blend = *data;
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use crate::definitions::{Directive, *};
use interner::StringKey;
use std::fmt::{Result as FormatResult, Write};

pub trait Schema {
    fn query_type(&self) -> Option<Type>;

    fn mutation_type(&self) -> Option<Type>;

    fn subscription_type(&self) -> Option<Type>;

    fn clientid_field(&self) -> FieldID;

    fn typename_field(&self) -> FieldID;

    fn fetch_token_field(&self) -> FieldID;

    fn get_type(&self, type_name: StringKey) -> Option<Type>;

    fn get_directive(&self, name: StringKey) -> Option<&Directive>;

    fn input_object(&self, id: InputObjectID) -> &InputObject;

    fn enum_(&self, id: EnumID) -> &Enum;

    fn scalar(&self, id: ScalarID) -> &Scalar;

    fn field(&self, id: FieldID) -> &Field;

    fn object(&self, id: ObjectID) -> &Object;

    fn union(&self, id: UnionID) -> ⋃

    fn interface(&self, id: InterfaceID) -> &Interface;

    fn get_type_name(&self, type_: Type) -> StringKey;

    fn is_extension_type(&self, type_: Type) -> bool;

    fn is_string(&self, type_: Type) -> bool;

    fn is_id(&self, type_: Type) -> bool;

    fn named_field(&self, parent_type: Type, name: StringKey) -> Option<FieldID>;

    fn unchecked_argument_type_sentinel(&self) -> &TypeReference;

    fn snapshot_print(self) -> String;

    fn is_type_subtype_of(
        &self,
        maybe_subtype: &TypeReference,
        super_type: &TypeReference,
    ) -> bool {
        match (maybe_subtype, super_type) {
            (TypeReference::NonNull(of_sub), TypeReference::NonNull(of_super)) => {
                self.is_type_subtype_of(of_sub, of_super)
            }
            // If supertype is non-null, maybeSubType must be non-nullable too
            (_, TypeReference::NonNull(_)) => false,
            // If supertype is nullable, maybeSubType may be non-nullable or nullable
            (TypeReference::NonNull(of_sub), _) => self.is_type_subtype_of(of_sub, super_type),
            (TypeReference::List(of_sub), TypeReference::List(of_super)) => {
                self.is_type_subtype_of(of_sub, of_super)
            }
            // If supertype is a list, maybeSubType must be a list too
            (_, TypeReference::List(_)) => false,
            // If supertype is not a list, maybeSubType must also not be a list
            (TypeReference::List(_), _) => false,
            (TypeReference::Named(named_subtype), TypeReference::Named(named_supertype)) => {
                self.is_named_type_subtype_of(*named_subtype, *named_supertype)
            }
        }
    }

    fn is_named_type_subtype_of(&self, maybe_subtype: Type, super_type: Type) -> bool {
        match (maybe_subtype, super_type) {
            (Type::Object(sub_id), Type::Interface(super_id)) => {
                // does object implement the interface
                let object = self.object(sub_id);
                object.interfaces.contains(&super_id)
            }
            (Type::Object(sub_id), Type::Union(super_id)) => {
                // is object a member of the union
                let union = self.union(super_id);
                union.members.contains(&sub_id)
            }
            (Type::Interface(sub_id), Type::Interface(super_id)) => {
                // does interface implement the interface
                let interface = self.interface(sub_id);
                sub_id == super_id || interface.interfaces.contains(&super_id)
            }
            _ => maybe_subtype == super_type,
        }
    }

    fn are_overlapping_types(&self, a: Type, b: Type) -> bool {
        if a == b {
            return true;
        };
        match (a, b) {
            (Type::Interface(a), Type::Interface(b)) => {
                let b_implementors = &self.interface(b).implementing_objects;
                self.interface(a)
                    .implementing_objects
                    .iter()
                    .any(|x| b_implementors.contains(x))
            }
            (Type::Interface(a), Type::Union(b)) => {
                let b_members = &self.union(b).members;
                self.interface(a)
                    .implementing_objects
                    .iter()
                    .any(|x| b_members.contains(x))
            }
            (Type::Interface(a), Type::Object(b)) => {
                self.interface(a).implementing_objects.contains(&b)
            }
            (Type::Union(a), Type::Interface(b)) => {
                let b_implementors = &self.interface(b).implementing_objects;
                self.union(a)
                    .members
                    .iter()
                    .any(|x| b_implementors.contains(x))
            }
            (Type::Union(a), Type::Union(b)) => {
                let b_members = &self.union(b).members;
                self.union(a).members.iter().any(|x| b_members.contains(x))
            }
            (Type::Union(a), Type::Object(b)) => self.union(a).members.contains(&b),
            (Type::Object(a), Type::Interface(b)) => {
                self.interface(b).implementing_objects.contains(&a)
            }
            (Type::Object(a), Type::Union(b)) => self.union(b).members.contains(&a),
            (Type::Object(a), Type::Object(b)) => a == b,
            _ => false, // todo: change Type representation to allow only Interface/Union/Object as input
        }
    }

    fn write_type_string<W: Write>(&self, writer: &mut W, type_: &TypeReference) -> FormatResult {
        match type_ {
            TypeReference::Named(inner) => {
                write!(writer, "{}", self.get_type_name(*inner).lookup())
            }
            TypeReference::NonNull(of) => {
                self.write_type_string(writer, of)?;
                write!(writer, "!")
            }
            TypeReference::List(of) => {
                write!(writer, "[")?;
                self.write_type_string(writer, of)?;
                write!(writer, "]")
            }
        }
    }

    fn get_type_string(&self, type_: &TypeReference) -> String {
        let mut result = String::new();
        self.write_type_string(&mut result, type_).unwrap();
        result
    }

    fn is_extension_directive(&self, name: StringKey) -> bool {
        if let Some(directive) = self.get_directive(name) {
            directive.is_extension
        } else {
            panic!("Unknown directive {}.", name.lookup())
        }
    }
}
<commit_msg>remove duplication in Schema::are_overlapping_types<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use crate::definitions::{Directive, *};
use interner::StringKey;
use std::fmt::{Result as FormatResult, Write};

pub trait Schema {
    fn query_type(&self) -> Option<Type>;

    fn mutation_type(&self) -> Option<Type>;

    fn subscription_type(&self) -> Option<Type>;

    fn clientid_field(&self) -> FieldID;

    fn typename_field(&self) -> FieldID;

    fn fetch_token_field(&self) -> FieldID;

    fn get_type(&self, type_name: StringKey) -> Option<Type>;

    fn get_directive(&self, name: StringKey) -> Option<&Directive>;

    fn input_object(&self, id: InputObjectID) -> &InputObject;

    fn enum_(&self, id: EnumID) -> &Enum;

    fn scalar(&self, id: ScalarID) -> &Scalar;

    fn field(&self, id: FieldID) -> &Field;

    fn object(&self, id: ObjectID) -> &Object;

    fn union(&self, id: UnionID) -> ⋃

    fn interface(&self, id: InterfaceID) -> &Interface;

    fn get_type_name(&self, type_: Type) -> StringKey;

    fn is_extension_type(&self, type_: Type) -> bool;

    fn is_string(&self, type_: Type) -> bool;

    fn is_id(&self, type_: Type) -> bool;

    fn named_field(&self, parent_type: Type, name: StringKey) -> Option<FieldID>;

    fn unchecked_argument_type_sentinel(&self) -> &TypeReference;

    fn snapshot_print(self) -> String;

    fn is_type_subtype_of(
        &self,
        maybe_subtype: &TypeReference,
        super_type: &TypeReference,
    ) -> bool {
        match (maybe_subtype, super_type) {
            (TypeReference::NonNull(of_sub), TypeReference::NonNull(of_super)) => {
                self.is_type_subtype_of(of_sub, of_super)
            }
            // If supertype is non-null, maybeSubType must be non-nullable too
            (_, TypeReference::NonNull(_)) => false,
            // If supertype is nullable, maybeSubType may be non-nullable or nullable
            (TypeReference::NonNull(of_sub), _) => self.is_type_subtype_of(of_sub, super_type),
            (TypeReference::List(of_sub), TypeReference::List(of_super)) => {
                self.is_type_subtype_of(of_sub, of_super)
            }
            // If supertype is a list, maybeSubType must be a list too
            (_, TypeReference::List(_)) => false,
            // If supertype is not a list, maybeSubType must also not be a list
            (TypeReference::List(_), _) => false,
            (TypeReference::Named(named_subtype), TypeReference::Named(named_supertype)) => {
                self.is_named_type_subtype_of(*named_subtype, *named_supertype)
            }
        }
    }

    fn is_named_type_subtype_of(&self, maybe_subtype: Type, super_type: Type) -> bool {
        match (maybe_subtype, super_type) {
            (Type::Object(sub_id), Type::Interface(super_id)) => {
                // does object implement the interface
                let object = self.object(sub_id);
                object.interfaces.contains(&super_id)
            }
            (Type::Object(sub_id), Type::Union(super_id)) => {
                // is object a member of the union
                let union = self.union(super_id);
                union.members.contains(&sub_id)
            }
            (Type::Interface(sub_id), Type::Interface(super_id)) => {
                // does interface implement the interface
                let interface = self.interface(sub_id);
                sub_id == super_id || interface.interfaces.contains(&super_id)
            }
            _ => maybe_subtype == super_type,
        }
    }

    fn are_overlapping_types(&self, a: Type, b: Type) -> bool {
        fn overlapping_objects(a: &[ObjectID], b: &[ObjectID]) -> bool {
            a.iter().any(|item| b.contains(item))
        }

        if a == b {
            return true;
        };
        match (a, b) {
            (Type::Interface(a), Type::Interface(b)) => overlapping_objects(
                &self.interface(a).implementing_objects,
                &self.interface(b).implementing_objects,
            ),

            (Type::Union(a), Type::Union(b)) => {
                overlapping_objects(&self.union(a).members, &self.union(b).members)
            }

            (Type::Union(union_id), Type::Interface(interface_id))
            | (Type::Interface(interface_id), Type::Union(union_id)) => overlapping_objects(
                &self.union(union_id).members,
                &self.interface(interface_id).implementing_objects,
            ),

            (Type::Interface(interface_id), Type::Object(object_id))
            | (Type::Object(object_id), Type::Interface(interface_id)) => self
                .interface(interface_id)
                .implementing_objects
                .contains(&object_id),

            (Type::Union(union_id), Type::Object(object_id))
            | (Type::Object(object_id), Type::Union(union_id)) => {
                self.union(union_id).members.contains(&object_id)
            }

            _ => false,
        }
    }

    fn write_type_string<W: Write>(&self, writer: &mut W, type_: &TypeReference) -> FormatResult {
        match type_ {
            TypeReference::Named(inner) => {
                write!(writer, "{}", self.get_type_name(*inner).lookup())
            }
            TypeReference::NonNull(of) => {
                self.write_type_string(writer, of)?;
                write!(writer, "!")
            }
            TypeReference::List(of) => {
                write!(writer, "[")?;
                self.write_type_string(writer, of)?;
                write!(writer, "]")
            }
        }
    }

    fn get_type_string(&self, type_: &TypeReference) -> String {
        let mut result = String::new();
        self.write_type_string(&mut result, type_).unwrap();
        result
    }

    fn is_extension_directive(&self, name: StringKey) -> bool {
        if let Some(directive) = self.get_directive(name) {
            directive.is_extension
        } else {
            panic!("Unknown directive {}.", name.lookup())
        }
    }
}
<|endoftext|>


<commit_before>// Examples from Eric's internship final presentation.
//
// Code is easier to write in emacs, and it's good to be sure all the
// code samples compile (or not) as they should.

// xfail-pretty

import double_buffer::client::*;
import double_buffer::give_buffer;

macro_rules! select_if {
    {
        $index:expr,
        $count:expr,
        $port:path => [
            $($message:path$(($($x: ident),+))dont_type_this*
              -> $next:ident $e:expr),+
        ],
        $( $ports:path => [
            $($messages:path$(($($xs: ident),+))dont_type_this*
              -> $nexts:ident $es:expr),+
        ], )*
    } => {
        log_syntax!{select_if1};
        if $index == $count {
            match move pipes::try_recv($port) {
              $(some($message($($(copy $x,)+)* next)) => {
                // FIXME (#2329) we really want move out of enum here.
                let $next = unsafe { let x <- *ptr::addr_of(next); x };
                $e
              })+
              _ => fail
            }
        } else {
            select_if!{
                $index,
                $count + 1,
                $( $ports => [
                    $($messages$(($($xs),+))dont_type_this*
                      -> $nexts $es),+
                ], )*
            }
        }
    };

    {
        $index:expr,
        $count:expr,
    } => {
        log_syntax!{select_if2};
        fail
    }
}

macro_rules! select {
    {
        $( $port:path => {
            $($message:path$(($($x: ident),+))dont_type_this*
              -> $next:ident $e:expr),+
        } )+
    } => {
        let index = pipes::selecti([$(($port).header()),+]/_);
        log_syntax!{select};
        log_syntax!{
        select_if!{index, 0, $( $port => [
            $($message$(($($x),+))dont_type_this* -> $next $e),+
        ], )+}
        };
        select_if!{index, 0, $( $port => [
            $($message$(($($x),+))dont_type_this* -> $next $e),+
        ], )+}
    }
}

// Types and protocols
struct Buffer {
    foo: ();

    drop { }
}

proto! double_buffer {
    acquire:send {
        request -> wait_buffer
    }

    wait_buffer:recv {
        give_buffer(Buffer) -> release
    }

    release:send {
        release(Buffer) -> acquire
    }
}

// Code examples
fn render(_buffer: &Buffer) {
    // A dummy function.
}

fn draw_frame(+channel: double_buffer::client::acquire) {
    let channel = request(channel);
    select! {
        channel => {
            give_buffer(buffer) -> channel {
                render(&buffer);
                release(channel, move buffer)
            }
        }
    };
}

fn draw_two_frames(+channel: double_buffer::client::acquire) {
    let channel = request(channel);
    let channel = select! {
        channel => {
            give_buffer(buffer) -> channel {
                render(&buffer);
                release(channel, move buffer)
            }
        }
    };
    let channel = request(channel);
    select! {
        channel => {
            give_buffer(buffer) -> channel {
                render(&buffer);
                release(channel, move buffer)
            }
        }
    };
}

#[cfg(bad1)]
fn draw_two_frames_bad1(+channel: double_buffer::client::acquire) {
    let channel = request(channel);
    let channel = select! {
        channel => {
            give_buffer(buffer) -> channel {
                render(&buffer);
                channel
            }
        }
    };
    let channel = request(channel);
    select! {
        channel => {
            give_buffer(buffer) -> channel {
                render(&buffer);
                release(channel, move buffer)
            }
        }
    };
}

#[cfg(bad2)]
fn draw_two_frames_bad2(+channel: double_buffer::client::acquire) {
    let channel = request(channel);
    select! {
        channel => {
            give_buffer(buffer) -> channel {
                render(&buffer);
                release(channel, move buffer);
                render(&buffer);
                release(channel, move buffer);
            }
        }
    };
}

fn main() { }<commit_msg>Updating examples<commit_after>// Examples from Eric's internship final presentation.
//
// Code is easier to write in emacs, and it's good to be sure all the
// code samples compile (or not) as they should.

// xfail-pretty

import double_buffer::client::*;
import double_buffer::give_buffer;

macro_rules! select_if {
    {
        $index:expr,
        $count:expr,
        $port:path => [
            $($message:path$(($($x: ident),+))dont_type_this*
              -> $next:ident $e:expr),+
        ],
        $( $ports:path => [
            $($messages:path$(($($xs: ident),+))dont_type_this*
              -> $nexts:ident $es:expr),+
        ], )*
    } => {
        if $index == $count {
            match move pipes::try_recv($port) {
              $(some($message($($(copy $x,)+)* next)) => {
                // FIXME (#2329) we really want move out of enum here.
                let $next = unsafe { let x <- *ptr::addr_of(next); x };
                $e
              })+
              _ => fail
            }
        } else {
            select_if!{
                $index,
                $count + 1,
                $( $ports => [
                    $($messages$(($($xs),+))dont_type_this*
                      -> $nexts $es),+
                ], )*
            }
        }
    };

    {
        $index:expr,
        $count:expr,
    } => {
        fail
    }
}

macro_rules! select {
    {
        $( $port:path => {
            $($message:path$(($($x: ident),+))dont_type_this*
              -> $next:ident $e:expr),+
        } )+
    } => {
        let index = pipes::selecti([$(($port).header()),+]/_);
        select_if!{index, 0, $( $port => [
            $($message$(($($x),+))dont_type_this* -> $next $e),+
        ], )+}
    }
}

// Types and protocols
struct Buffer {
    foo: ();

    drop { }
}

proto! double_buffer {
    acquire:send {
        request -> wait_buffer
    }

    wait_buffer:recv {
        give_buffer(Buffer) -> release
    }

    release:send {
        release(Buffer) -> acquire
    }
}

// Code examples
fn render(_buffer: &Buffer) {
    // A dummy function.
}

fn draw_frame(+channel: double_buffer::client::acquire) {
    let channel = request(channel);
    select! {
        channel => {
            give_buffer(buffer) -> channel {
                render(&buffer);
                release(channel, move buffer)
            }
        }
    };
}

fn draw_two_frames(+channel: double_buffer::client::acquire) {
    let channel = request(channel);
    let channel = select! {
        channel => {
            give_buffer(buffer) -> channel {
                render(&buffer);
                release(channel, move buffer)
            }
        }
    };
    let channel = request(channel);
    select! {
        channel => {
            give_buffer(buffer) -> channel {
                render(&buffer);
                release(channel, move buffer)
            }
        }
    };
}

#[cfg(bad1)]
fn draw_two_frames_bad1(+channel: double_buffer::client::acquire) {
    let channel = request(channel);
    select! {
        channel => {
            give_buffer(buffer) -> channel {
                render(&buffer);
            }
        }
    };
    let channel = request(channel);
    select! {
        channel => {
            give_buffer(buffer) -> channel {
                render(&buffer);
                release(channel, move buffer)
            }
        }
    };
}

#[cfg(bad2)]
fn draw_two_frames_bad2(+channel: double_buffer::client::acquire) {
    let channel = request(channel);
    select! {
        channel => {
            give_buffer(buffer) -> channel {
                render(&buffer);
                release(channel, move buffer);
                render(&buffer);
                release(channel, move buffer);
            }
        }
    };
}

fn main() { }
<|endoftext|>


<commit_before>use if_chain::if_chain;
use rustc_hir::{Local, PatKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::subst::GenericArgKind;
use rustc_session::{declare_lint_pass, declare_tool_lint};

use crate::utils::{is_must_use_func_call, is_must_use_ty, match_type, paths, span_lint_and_help};

declare_clippy_lint! {
    /// **What it does:** Checks for `let _ = <expr>`
    /// where expr is #[must_use]
    ///
    /// **Why is this bad?** It's better to explicitly
    /// handle the value of a #[must_use] expr
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// fn f() -> Result<u32, u32> {
    ///     Ok(0)
    /// }
    ///
    /// let _ = f();
    /// // is_ok() is marked #[must_use]
    /// let _ = f().is_ok();
    /// ```
    pub LET_UNDERSCORE_MUST_USE,
    restriction,
    "non-binding let on a `#[must_use]` expression"
}

declare_clippy_lint! {
    /// **What it does:** Checks for `let _ = sync_lock`
    ///
    /// **Why is this bad?** This statement immediately drops the lock instead of
    /// extending it's lifetime to the end of the scope, which is often not intended.
    /// To extend lock lifetime to the end of the scope, use an underscore-prefixed
    /// name instead (i.e. _lock). If you want to explicitly drop the lock,
    /// `std::mem::drop` conveys your intention better and is less error-prone.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    ///
    /// Bad:
    /// ```rust,ignore
    /// let _ = mutex.lock();
    /// ```
    ///
    /// Good:
    /// ```rust,ignore
    /// let _lock = mutex.lock();
    /// ```
    pub LET_UNDERSCORE_LOCK,
    correctness,
    "non-binding let on a synchronization lock"
}

declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK]);

const SYNC_GUARD_PATHS: [&[&str]; 3] = [
    &paths::MUTEX_GUARD,
    &paths::RWLOCK_READ_GUARD,
    &paths::RWLOCK_WRITE_GUARD,
];

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LetUnderscore {
    fn check_local(&mut self, cx: &LateContext<'_, '_>, local: &Local<'_>) {
        if in_external_macro(cx.tcx.sess, local.span) {
            return;
        }

        if_chain! {
            if let PatKind::Wild = local.pat.kind;
            if let Some(ref init) = local.init;
            then {
                let init_ty = cx.tables.expr_ty(init);
                let contains_sync_guard = init_ty.walk().any(|inner| match inner.unpack() {
                    GenericArgKind::Type(inner_ty) => {
                        SYNC_GUARD_PATHS.iter().any(|path| match_type(cx, inner_ty, path))
                    },

                    GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
                });
                if contains_sync_guard {
                    span_lint_and_help(
                        cx,
                        LET_UNDERSCORE_LOCK,
                        local.span,
                        "non-binding let on a synchronization lock",
                        None,
                        "consider using an underscore-prefixed named \
                            binding or dropping explicitly with `std::mem::drop`"
                    )
                } else if is_must_use_ty(cx, cx.tables.expr_ty(init)) {
                    span_lint_and_help(
                        cx,
                        LET_UNDERSCORE_MUST_USE,
                        local.span,
                        "non-binding let on an expression with `#[must_use]` type",
                        None,
                        "consider explicitly using expression value"
                    )
                } else if is_must_use_func_call(cx, init) {
                    span_lint_and_help(
                        cx,
                        LET_UNDERSCORE_MUST_USE,
                        local.span,
                        "non-binding let on a result of a `#[must_use]` function",
                        None,
                        "consider explicitly using function result"
                    )
                }
            }
        }
    }
}
<commit_msg>Fix typo<commit_after>use if_chain::if_chain;
use rustc_hir::{Local, PatKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::subst::GenericArgKind;
use rustc_session::{declare_lint_pass, declare_tool_lint};

use crate::utils::{is_must_use_func_call, is_must_use_ty, match_type, paths, span_lint_and_help};

declare_clippy_lint! {
    /// **What it does:** Checks for `let _ = <expr>`
    /// where expr is #[must_use]
    ///
    /// **Why is this bad?** It's better to explicitly
    /// handle the value of a #[must_use] expr
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// fn f() -> Result<u32, u32> {
    ///     Ok(0)
    /// }
    ///
    /// let _ = f();
    /// // is_ok() is marked #[must_use]
    /// let _ = f().is_ok();
    /// ```
    pub LET_UNDERSCORE_MUST_USE,
    restriction,
    "non-binding let on a `#[must_use]` expression"
}

declare_clippy_lint! {
    /// **What it does:** Checks for `let _ = sync_lock`
    ///
    /// **Why is this bad?** This statement immediately drops the lock instead of
    /// extending its lifetime to the end of the scope, which is often not intended.
    /// To extend lock lifetime to the end of the scope, use an underscore-prefixed
    /// name instead (i.e. _lock). If you want to explicitly drop the lock,
    /// `std::mem::drop` conveys your intention better and is less error-prone.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    ///
    /// Bad:
    /// ```rust,ignore
    /// let _ = mutex.lock();
    /// ```
    ///
    /// Good:
    /// ```rust,ignore
    /// let _lock = mutex.lock();
    /// ```
    pub LET_UNDERSCORE_LOCK,
    correctness,
    "non-binding let on a synchronization lock"
}

declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK]);

const SYNC_GUARD_PATHS: [&[&str]; 3] = [
    &paths::MUTEX_GUARD,
    &paths::RWLOCK_READ_GUARD,
    &paths::RWLOCK_WRITE_GUARD,
];

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LetUnderscore {
    fn check_local(&mut self, cx: &LateContext<'_, '_>, local: &Local<'_>) {
        if in_external_macro(cx.tcx.sess, local.span) {
            return;
        }

        if_chain! {
            if let PatKind::Wild = local.pat.kind;
            if let Some(ref init) = local.init;
            then {
                let init_ty = cx.tables.expr_ty(init);
                let contains_sync_guard = init_ty.walk().any(|inner| match inner.unpack() {
                    GenericArgKind::Type(inner_ty) => {
                        SYNC_GUARD_PATHS.iter().any(|path| match_type(cx, inner_ty, path))
                    },

                    GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
                });
                if contains_sync_guard {
                    span_lint_and_help(
                        cx,
                        LET_UNDERSCORE_LOCK,
                        local.span,
                        "non-binding let on a synchronization lock",
                        None,
                        "consider using an underscore-prefixed named \
                            binding or dropping explicitly with `std::mem::drop`"
                    )
                } else if is_must_use_ty(cx, cx.tables.expr_ty(init)) {
                    span_lint_and_help(
                        cx,
                        LET_UNDERSCORE_MUST_USE,
                        local.span,
                        "non-binding let on an expression with `#[must_use]` type",
                        None,
                        "consider explicitly using expression value"
                    )
                } else if is_must_use_func_call(cx, init) {
                    span_lint_and_help(
                        cx,
                        LET_UNDERSCORE_MUST_USE,
                        local.span,
                        "non-binding let on a result of a `#[must_use]` function",
                        None,
                        "consider explicitly using function result"
                    )
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use crate::{
    metadata::{view::MetadataView, Metadata},
    metrics::metadata::{NUM_META_DOWNLOAD, NUM_META_FILES, NUM_META_MISS},
    storage::{BackupStorage, FileHandle},
    utils::{error_notes::ErrorNotes, stream::StreamX},
};
use anyhow::{anyhow, Result};
use aptos_logger::prelude::*;
use aptos_temppath::TempPath;
use async_trait::async_trait;
use clap::Parser;
use futures::stream::poll_fn;
use once_cell::sync::Lazy;
use std::{
    collections::{HashMap, HashSet},
    path::PathBuf,
    sync::Arc,
    time::Instant,
};
use tokio::{
    fs::{create_dir_all, read_dir, remove_file, OpenOptions},
    io::{AsyncRead, AsyncReadExt},
};
use tokio_stream::StreamExt;

static TEMP_METADATA_CACHE_DIR: Lazy<TempPath> = Lazy::new(|| {
    let dir = TempPath::new();
    dir.create_as_dir()
        .expect("Temp metadata dir should create.");
    dir
});

#[derive(Parser)]
pub struct MetadataCacheOpt {
    #[clap(
        long = "metadata-cache-dir",
        parse(from_os_str),
        help = "[Defaults to temporary dir] Metadata cache dir. If specified and shared across runs, \
        metadata files in cache won't be downloaded again from backup source, speeding up tool \
        boot up significantly."
    )]
    dir: Option<PathBuf>,
}

impl MetadataCacheOpt {
    // in cache we save things other than the cached files.
    const SUB_DIR: &'static str = "cache";

    fn cache_dir(&self) -> PathBuf {
        self.dir
            .clone()
            .unwrap_or_else(|| TEMP_METADATA_CACHE_DIR.path().to_path_buf())
            .join(Self::SUB_DIR)
    }
}

/// Sync local cache folder with remote storage, and load all metadata entries from the cache.
pub async fn sync_and_load(
    opt: &MetadataCacheOpt,
    storage: Arc<dyn BackupStorage>,
    concurrent_downloads: usize,
) -> Result<MetadataView> {
    let timer = Instant::now();
    let cache_dir = opt.cache_dir();
    create_dir_all(&cache_dir).await.err_notes(&cache_dir)?; // create if not present already

    // List cached metadata files.
    let mut dir = read_dir(&cache_dir).await.err_notes(&cache_dir)?;
    let local_entries = poll_fn(|ctx| {
        ::std::task::Poll::Ready(match futures::ready!(dir.poll_next_entry(ctx)) {
            Ok(Some(entry)) => Some(Ok(entry)),
            Ok(None) => None,
            Err(err) => Some(Err(err)),
        })
    })
    .collect::<tokio::io::Result<Vec<_>>>()
    .await?;
    let local_hashes = local_entries
        .iter()
        .map(|e| {
            e.file_name()
                .into_string()
                .map_err(|s| anyhow!("into_string() failed for file name {:?}", s))
        })
        .collect::<Result<HashSet<_>>>()?;

    // List remote metadata files.
    let remote_file_handles = storage.list_metadata_files().await?;
    let remote_file_handle_by_hash: HashMap<_, _> = remote_file_handles
        .into_iter()
        .map(|file_handle| (file_handle.file_handle_hash(), file_handle))
        .collect();
    let remote_hashes: HashSet<_> = remote_file_handle_by_hash.keys().cloned().collect();
    info!("Metadata files listed.");
    NUM_META_FILES.set(remote_hashes.len() as i64);

    // Sync local cache with remote metadata files.
    let stale_local_hashes = local_hashes.difference(&remote_hashes);
    let new_remote_hashes = remote_hashes.difference(&local_hashes).collect::<Vec<_>>();
    let up_to_date_local_hashes = local_hashes.intersection(&remote_hashes);

    for h in stale_local_hashes {
        let file = cache_dir.join(h);
        remove_file(&file).await.err_notes(&file)?;
    }

    NUM_META_MISS.set(new_remote_hashes.len() as i64);
    NUM_META_DOWNLOAD.set(0);
    let futs = new_remote_hashes.iter().map(|h| {
        let fh_by_h_ref = &remote_file_handle_by_hash;
        let storage_ref = &storage;
        let cache_dir_ref = &cache_dir;

        async move {
            let file_handle = fh_by_h_ref.get(*h).expect("In map.");
            let local_file = cache_dir_ref.join(*h);
            let local_tmp_file = cache_dir_ref.join(format!(".{}", *h));
            // download to tmp file ".xxxxxx"
            tokio::io::copy(
                &mut storage_ref
                    .open_for_read(file_handle)
                    .await
                    .err_notes(file_handle)?,
                &mut OpenOptions::new()
                    .write(true)
                    .create_new(true)
                    .open(&local_tmp_file)
                    .await
                    .err_notes(&local_file)?,
            )
            .await?;
            // rename to target file only if successful; stale tmp file caused by failure will be
            // reclaimed on next run
            tokio::fs::rename(local_tmp_file, local_file).await?;
            NUM_META_DOWNLOAD.inc();
            Ok(())
        }
    });
    futures::stream::iter(futs)
        .buffered_x(
            concurrent_downloads * 2, /* buffer size */
            concurrent_downloads,     /* concurrency */
        )
        .collect::<Result<Vec<_>>>()
        .await?;

    // Load metadata from synced cache files.
    let mut metadata_vec = Vec::new();
    for h in new_remote_hashes.into_iter().chain(up_to_date_local_hashes) {
        let cached_file = cache_dir.join(h);
        metadata_vec.extend(
            OpenOptions::new()
                .read(true)
                .open(&cached_file)
                .await
                .err_notes(&cached_file)?
                .load_metadata_lines()
                .await
                .err_notes(&cached_file)?
                .into_iter(),
        )
    }
    info!(
        "Metadata cache loaded in {:.2} seconds.",
        timer.elapsed().as_secs_f64()
    );
    Ok(metadata_vec.into())
}

trait FileHandleHash {
    fn file_handle_hash(&self) -> String;
}

impl FileHandleHash for FileHandle {
    fn file_handle_hash(&self) -> String {
        use std::hash::{Hash, Hasher};

        let mut hasher = std::collections::hash_map::DefaultHasher::new();
        self.hash(&mut hasher);
        format!("{:x}", hasher.finish())
    }
}

#[async_trait]
trait LoadMetadataLines {
    async fn load_metadata_lines(&mut self) -> Result<Vec<Metadata>>;
}

#[async_trait]
impl<R: AsyncRead + Send + Unpin> LoadMetadataLines for R {
    async fn load_metadata_lines(&mut self) -> Result<Vec<Metadata>> {
        let mut buf = String::new();
        self.read_to_string(&mut buf)
            .await
            .err_notes((file!(), line!(), &buf))?;
        Ok(buf
            .lines()
            .map(serde_json::from_str::<Metadata>)
            .collect::<Result<_, serde_json::error::Error>>()?)
    }
}
<commit_msg>[backup] report progress loading metadata cache<commit_after>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use crate::{
    metadata::{view::MetadataView, Metadata},
    metrics::metadata::{NUM_META_DOWNLOAD, NUM_META_FILES, NUM_META_MISS},
    storage::{BackupStorage, FileHandle},
    utils::{error_notes::ErrorNotes, stream::StreamX},
};
use anyhow::{anyhow, Result};
use aptos_logger::prelude::*;
use aptos_temppath::TempPath;
use async_trait::async_trait;
use clap::Parser;
use futures::stream::poll_fn;
use once_cell::sync::Lazy;
use std::{
    collections::{HashMap, HashSet},
    path::PathBuf,
    sync::Arc,
    time::Instant,
};
use tokio::{
    fs::{create_dir_all, read_dir, remove_file, OpenOptions},
    io::{AsyncRead, AsyncReadExt},
};
use tokio_stream::StreamExt;

static TEMP_METADATA_CACHE_DIR: Lazy<TempPath> = Lazy::new(|| {
    let dir = TempPath::new();
    dir.create_as_dir()
        .expect("Temp metadata dir should create.");
    dir
});

#[derive(Parser)]
pub struct MetadataCacheOpt {
    #[clap(
        long = "metadata-cache-dir",
        parse(from_os_str),
        help = "[Defaults to temporary dir] Metadata cache dir. If specified and shared across runs, \
        metadata files in cache won't be downloaded again from backup source, speeding up tool \
        boot up significantly."
    )]
    dir: Option<PathBuf>,
}

impl MetadataCacheOpt {
    // in cache we save things other than the cached files.
    const SUB_DIR: &'static str = "cache";

    fn cache_dir(&self) -> PathBuf {
        self.dir
            .clone()
            .unwrap_or_else(|| TEMP_METADATA_CACHE_DIR.path().to_path_buf())
            .join(Self::SUB_DIR)
    }
}

/// Sync local cache folder with remote storage, and load all metadata entries from the cache.
pub async fn sync_and_load(
    opt: &MetadataCacheOpt,
    storage: Arc<dyn BackupStorage>,
    concurrent_downloads: usize,
) -> Result<MetadataView> {
    let timer = Instant::now();
    let cache_dir = opt.cache_dir();
    create_dir_all(&cache_dir).await.err_notes(&cache_dir)?; // create if not present already

    // List cached metadata files.
    let mut dir = read_dir(&cache_dir).await.err_notes(&cache_dir)?;
    let local_entries = poll_fn(|ctx| {
        ::std::task::Poll::Ready(match futures::ready!(dir.poll_next_entry(ctx)) {
            Ok(Some(entry)) => Some(Ok(entry)),
            Ok(None) => None,
            Err(err) => Some(Err(err)),
        })
    })
    .collect::<tokio::io::Result<Vec<_>>>()
    .await?;
    let local_hashes = local_entries
        .iter()
        .map(|e| {
            e.file_name()
                .into_string()
                .map_err(|s| anyhow!("into_string() failed for file name {:?}", s))
        })
        .collect::<Result<HashSet<_>>>()?;

    // List remote metadata files.
    let remote_file_handles = storage.list_metadata_files().await?;
    let remote_file_handle_by_hash: HashMap<_, _> = remote_file_handles
        .into_iter()
        .map(|file_handle| (file_handle.file_handle_hash(), file_handle))
        .collect();
    let remote_hashes: HashSet<_> = remote_file_handle_by_hash.keys().cloned().collect();
    info!("Metadata files listed.");
    NUM_META_FILES.set(remote_hashes.len() as i64);

    // Sync local cache with remote metadata files.
    let stale_local_hashes = local_hashes.difference(&remote_hashes);
    let new_remote_hashes = remote_hashes.difference(&local_hashes).collect::<Vec<_>>();
    let up_to_date_local_hashes = local_hashes.intersection(&remote_hashes);

    for h in stale_local_hashes {
        let file = cache_dir.join(h);
        remove_file(&file).await.err_notes(&file)?;
    }
    info!("Deleted stale metadata files in cache.");

    let num_new_files = new_remote_hashes.len();
    NUM_META_MISS.set(num_new_files as i64);
    NUM_META_DOWNLOAD.set(0);
    let futs = new_remote_hashes.iter().enumerate().map(|(i, h)| {
        let fh_by_h_ref = &remote_file_handle_by_hash;
        let storage_ref = &storage;
        let cache_dir_ref = &cache_dir;

        async move {
            let file_handle = fh_by_h_ref.get(*h).expect("In map.");
            let local_file = cache_dir_ref.join(*h);
            let local_tmp_file = cache_dir_ref.join(format!(".{}", *h));
            // download to tmp file ".xxxxxx"
            tokio::io::copy(
                &mut storage_ref
                    .open_for_read(file_handle)
                    .await
                    .err_notes(file_handle)?,
                &mut OpenOptions::new()
                    .write(true)
                    .create_new(true)
                    .open(&local_tmp_file)
                    .await
                    .err_notes(&local_file)?,
            )
            .await?;
            // rename to target file only if successful; stale tmp file caused by failure will be
            // reclaimed on next run
            tokio::fs::rename(local_tmp_file, local_file).await?;
            info!(
                file_handle = file_handle,
                processed = i + 1,
                total = num_new_files,
                "Metadata file downloaded."
            );
            NUM_META_DOWNLOAD.inc();
            Ok(())
        }
    });
    futures::stream::iter(futs)
        .buffered_x(
            concurrent_downloads * 2, /* buffer size */
            concurrent_downloads,     /* concurrency */
        )
        .collect::<Result<Vec<_>>>()
        .await?;

    info!("Loading all metadata files to memory.");
    // Load metadata from synced cache files.
    let mut metadata_vec = Vec::new();
    for h in new_remote_hashes.into_iter().chain(up_to_date_local_hashes) {
        let cached_file = cache_dir.join(h);
        metadata_vec.extend(
            OpenOptions::new()
                .read(true)
                .open(&cached_file)
                .await
                .err_notes(&cached_file)?
                .load_metadata_lines()
                .await
                .err_notes(&cached_file)?
                .into_iter(),
        )
    }
    info!(
        total_time = timer.elapsed().as_secs(),
        "Metadata cache loaded.",
    );
    Ok(metadata_vec.into())
}

trait FileHandleHash {
    fn file_handle_hash(&self) -> String;
}

impl FileHandleHash for FileHandle {
    fn file_handle_hash(&self) -> String {
        use std::hash::{Hash, Hasher};

        let mut hasher = std::collections::hash_map::DefaultHasher::new();
        self.hash(&mut hasher);
        format!("{:x}", hasher.finish())
    }
}

#[async_trait]
trait LoadMetadataLines {
    async fn load_metadata_lines(&mut self) -> Result<Vec<Metadata>>;
}

#[async_trait]
impl<R: AsyncRead + Send + Unpin> LoadMetadataLines for R {
    async fn load_metadata_lines(&mut self) -> Result<Vec<Metadata>> {
        let mut buf = String::new();
        self.read_to_string(&mut buf)
            .await
            .err_notes((file!(), line!(), &buf))?;
        Ok(buf
            .lines()
            .map(serde_json::from_str::<Metadata>)
            .collect::<Result<_, serde_json::error::Error>>()?)
    }
}
<|endoftext|>


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

#![crate_name = "gfx_traits"]
#![crate_type = "rlib"]

#![deny(unsafe_code)]

extern crate malloc_size_of;
#[macro_use] extern crate malloc_size_of_derive;
#[macro_use] extern crate range;
#[macro_use] extern crate serde;

pub mod print_tree;

use range::RangeIndex;
use std::sync::atomic::{ATOMIC_USIZE_INIT, AtomicUsize, Ordering};

/// A newtype struct for denoting the age of messages; prevents race conditions.
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
pub struct Epoch(pub u32);

impl Epoch {
    pub fn next(&mut self) {
        self.0 += 1;
    }
}

/// A unique ID for every stacking context.
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
pub struct StackingContextId(
    /// The identifier for this StackingContext, derived from the Flow's memory address
    /// and fragment type.  As a space optimization, these are combined into a single word.
    pub u64
);

impl StackingContextId {
    /// Returns the stacking context ID for the outer document/layout root.
    #[inline]
    pub fn root() -> StackingContextId {
        StackingContextId(0)
    }

    pub fn next(&self) -> StackingContextId {
        let StackingContextId(id) = *self;
        StackingContextId(id + 1)
    }
}

int_range_index! {
    #[derive(Deserialize, Serialize)]
    #[doc = "An index that refers to a byte offset in a text run. This could \
             point to the middle of a glyph."]
    #[derive(MallocSizeOf)]
    struct ByteIndex(isize)
}

/// The type of fragment that a scroll root is created for.
///
/// This can only ever grow to maximum 4 entries. That's because we cram the value of this enum
/// into the lower 2 bits of the `ScrollRootId`, which otherwise contains a 32-bit-aligned
/// heap address.
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
pub enum FragmentType {
    /// A StackingContext for the fragment body itself.
    FragmentBody,
    /// A StackingContext created to contain ::before pseudo-element content.
    BeforePseudoContent,
    /// A StackingContext created to contain ::after pseudo-element content.
    AfterPseudoContent,
}

/// The next ID that will be used for a special scroll root id.
///
/// A special scroll root is a scroll root that is created for generated content.
static NEXT_SPECIAL_SCROLL_ROOT_ID: AtomicUsize = ATOMIC_USIZE_INIT;

/// If none of the bits outside this mask are set, the scroll root is a special scroll root.
/// Note that we assume that the top 16 bits of the address space are unused on the platform.
const SPECIAL_SCROLL_ROOT_ID_MASK: usize = 0xffff;

/// Returns a new scroll root ID for a scroll root.
fn next_special_id() -> usize {
    // We shift this left by 2 to make room for the fragment type ID.
    ((NEXT_SPECIAL_SCROLL_ROOT_ID.fetch_add(1, Ordering::SeqCst) + 1) << 2) &
        SPECIAL_SCROLL_ROOT_ID_MASK
}

pub fn combine_id_with_fragment_type(id: usize, fragment_type: FragmentType) ->  usize {
    debug_assert_eq!(id & (fragment_type as usize), 0);
    if fragment_type == FragmentType::FragmentBody {
        id
    } else {
        next_special_id() | (fragment_type as usize)
    }
}

pub fn node_id_from_scroll_id(id: usize) -> Option<usize> {
    if (id & !SPECIAL_SCROLL_ROOT_ID_MASK) != 0 {
        return Some((id & !3) as usize);
    }
    None
}

pub trait DisplayList {
    /// Returns true if this display list contains meaningful content.
    fn is_contentful(&self) -> bool;
}
<commit_msg>Format gfx_traits #21373<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/. */

#![crate_name = "gfx_traits"]
#![crate_type = "rlib"]
#![deny(unsafe_code)]

extern crate malloc_size_of;
#[macro_use]
extern crate malloc_size_of_derive;
#[macro_use]
extern crate range;
#[macro_use]
extern crate serde;

pub mod print_tree;

use range::RangeIndex;
use std::sync::atomic::{ATOMIC_USIZE_INIT, AtomicUsize, Ordering};

/// A newtype struct for denoting the age of messages; prevents race conditions.
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
pub struct Epoch(pub u32);

impl Epoch {
    pub fn next(&mut self) {
        self.0 += 1;
    }
}

/// A unique ID for every stacking context.
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
pub struct StackingContextId(
    /// The identifier for this StackingContext, derived from the Flow's memory address
    /// and fragment type.  As a space optimization, these are combined into a single word.
    pub u64,
);

impl StackingContextId {
    /// Returns the stacking context ID for the outer document/layout root.
    #[inline]
    pub fn root() -> StackingContextId {
        StackingContextId(0)
    }

    pub fn next(&self) -> StackingContextId {
        let StackingContextId(id) = *self;
        StackingContextId(id + 1)
    }
}

int_range_index! {
    #[derive(Deserialize, Serialize)]
    #[doc = "An index that refers to a byte offset in a text run. This could \
             point to the middle of a glyph."]
    #[derive(MallocSizeOf)]
    struct ByteIndex(isize)
}

/// The type of fragment that a scroll root is created for.
///
/// This can only ever grow to maximum 4 entries. That's because we cram the value of this enum
/// into the lower 2 bits of the `ScrollRootId`, which otherwise contains a 32-bit-aligned
/// heap address.
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
pub enum FragmentType {
    /// A StackingContext for the fragment body itself.
    FragmentBody,
    /// A StackingContext created to contain ::before pseudo-element content.
    BeforePseudoContent,
    /// A StackingContext created to contain ::after pseudo-element content.
    AfterPseudoContent,
}

/// The next ID that will be used for a special scroll root id.
///
/// A special scroll root is a scroll root that is created for generated content.
static NEXT_SPECIAL_SCROLL_ROOT_ID: AtomicUsize = ATOMIC_USIZE_INIT;

/// If none of the bits outside this mask are set, the scroll root is a special scroll root.
/// Note that we assume that the top 16 bits of the address space are unused on the platform.
const SPECIAL_SCROLL_ROOT_ID_MASK: usize = 0xffff;

/// Returns a new scroll root ID for a scroll root.
fn next_special_id() -> usize {
    // We shift this left by 2 to make room for the fragment type ID.
    ((NEXT_SPECIAL_SCROLL_ROOT_ID.fetch_add(1, Ordering::SeqCst) + 1) << 2) &
        SPECIAL_SCROLL_ROOT_ID_MASK
}

pub fn combine_id_with_fragment_type(id: usize, fragment_type: FragmentType) -> usize {
    debug_assert_eq!(id & (fragment_type as usize), 0);
    if fragment_type == FragmentType::FragmentBody {
        id
    } else {
        next_special_id() | (fragment_type as usize)
    }
}

pub fn node_id_from_scroll_id(id: usize) -> Option<usize> {
    if (id & !SPECIAL_SCROLL_ROOT_ID_MASK) != 0 {
        return Some((id & !3) as usize);
    }
    None
}

pub trait DisplayList {
    /// Returns true if this display list contains meaningful content.
    fn is_contentful(&self) -> bool;
}
<|endoftext|>


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

//! Data needed by the layout thread.

// for thread_local
#![allow(unsafe_code)]

use app_units::Au;
use canvas_traits::CanvasMsg;
use euclid::Rect;
use fnv::FnvHasher;
use gfx::font_cache_thread::FontCacheThread;
use gfx::font_context::FontContext;
use gfx_traits::LayerId;
use heapsize::HeapSizeOf;
use ipc_channel::ipc::{self, IpcSender};
use net_traits::image::base::Image;
use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheThread, ImageResponse, ImageState};
use net_traits::image_cache_thread::{ImageOrMetadataAvailable, UsePlaceholder};
use std::cell::{RefCell, RefMut};
use std::collections::HashMap;
use std::hash::BuildHasherDefault;
use std::rc::Rc;
use std::sync::mpsc::Sender;
use std::sync::{Arc, Mutex};
use style::context::{LocalStyleContext, StyleContext};
use style::matching::{ApplicableDeclarationsCache, StyleSharingCandidateCache};
use style::selector_impl::ServoSelectorImpl;
use style::servo::SharedStyleContext;
use url::Url;
use util::opts;

struct LocalLayoutContext {
    style_context: LocalStyleContext,
    font_context: RefCell<FontContext>,
}

impl HeapSizeOf for LocalLayoutContext {
    // FIXME(njn): measure other fields eventually.
    fn heap_size_of_children(&self) -> usize {
        self.font_context.heap_size_of_children()
    }
}

thread_local!(static LOCAL_CONTEXT_KEY: RefCell<Option<Rc<LocalLayoutContext>>> = RefCell::new(None));

pub fn heap_size_of_local_context() -> usize {
    LOCAL_CONTEXT_KEY.with(|r| {
        r.borrow().clone().map_or(0, |context| context.heap_size_of_children())
    })
}

// Keep this implementation in sync with the one in ports/geckolib/traversal.rs.
fn create_or_get_local_context(shared_layout_context: &SharedLayoutContext)
                               -> Rc<LocalLayoutContext> {
    LOCAL_CONTEXT_KEY.with(|r| {
        let mut r = r.borrow_mut();
        if let Some(context) = r.clone() {
            if shared_layout_context.style_context.screen_size_changed {
                context.style_context.applicable_declarations_cache.borrow_mut().evict_all();
            }
            context
        } else {
            let font_cache_thread = shared_layout_context.font_cache_thread.lock().unwrap().clone();
            let context = Rc::new(LocalLayoutContext {
                style_context: LocalStyleContext {
                    applicable_declarations_cache: RefCell::new(ApplicableDeclarationsCache::new()),
                    style_sharing_candidate_cache: RefCell::new(StyleSharingCandidateCache::new()),
                },
                font_context: RefCell::new(FontContext::new(font_cache_thread)),
            });
            *r = Some(context.clone());
            context
        }
    })
}

/// Layout information shared among all workers. This must be thread-safe.
pub struct SharedLayoutContext {
    /// Bits shared by the layout and style system.
    pub style_context: SharedStyleContext,

    /// The shared image cache thread.
    pub image_cache_thread: ImageCacheThread,

    /// A channel for the image cache to send responses to.
    pub image_cache_sender: Mutex<ImageCacheChan>,

    /// Interface to the font cache thread.
    pub font_cache_thread: Mutex<FontCacheThread>,

    /// The URL.
    pub url: Url,

    /// A channel to send canvas renderers to paint thread, in order to correctly paint the layers
    pub canvas_layers_sender: Mutex<Sender<(LayerId, IpcSender<CanvasMsg>)>>,

    /// The visible rects for each layer, as reported to us by the compositor.
    pub visible_rects: Arc<HashMap<LayerId, Rect<Au>, BuildHasherDefault<FnvHasher>>>,
}

pub struct LayoutContext<'a> {
    pub shared: &'a SharedLayoutContext,
    cached_local_layout_context: Rc<LocalLayoutContext>,
}

impl<'a> StyleContext<'a, ServoSelectorImpl> for LayoutContext<'a> {
    fn shared_context(&self) -> &'a SharedStyleContext {
        &self.shared.style_context
    }

    fn local_context(&self) -> &LocalStyleContext {
        &self.cached_local_layout_context.style_context
    }
}

impl<'a> LayoutContext<'a> {
    pub fn new(shared_layout_context: &'a SharedLayoutContext) -> LayoutContext<'a> {

        let local_context = create_or_get_local_context(shared_layout_context);

        LayoutContext {
            shared: shared_layout_context,
            cached_local_layout_context: local_context,
        }
    }

    #[inline(always)]
    pub fn font_context(&self) -> RefMut<FontContext> {
        self.cached_local_layout_context.font_context.borrow_mut()
    }

    pub fn get_or_request_image(&self, url: Url, use_placeholder: UsePlaceholder)
                                -> Option<Arc<Image>> {
        // See if the image is already available
        let result = self.shared.image_cache_thread.find_image(url.clone(),
                                                             use_placeholder);

        match result {
            Ok(image) => Some(image),
            Err(state) => {
                // If we are emitting an output file, then we need to block on
                // image load or we risk emitting an output file missing the image.
                let is_sync = opts::get().output_file.is_some() ||
                              opts::get().exit_after_load;

                match (state, is_sync) {
                    // Image failed to load, so just return nothing
                    (ImageState::LoadError, _) => None,
                    // Not loaded, test mode - load the image synchronously
                    (_, true) => {
                        let (sync_tx, sync_rx) = ipc::channel().unwrap();
                        self.shared.image_cache_thread.request_image(url,
                                                                   ImageCacheChan(sync_tx),
                                                                   None);
                        match sync_rx.recv().unwrap().image_response {
                            ImageResponse::Loaded(image) |
                            ImageResponse::PlaceholderLoaded(image) => Some(image),
                            ImageResponse::None | ImageResponse::MetadataLoaded(_) => None,
                        }
                    }
                    // Not yet requested, async mode - request image from the cache
                    (ImageState::NotRequested, false) => {
                        let sender = self.shared.image_cache_sender.lock().unwrap().clone();
                        self.shared.image_cache_thread.request_image(url, sender, None);
                        None
                    }
                    // Image has been requested, is still pending. Return no image
                    // for this paint loop. When the image loads it will trigger
                    // a reflow and/or repaint.
                    (ImageState::Pending, false) => None,
                }
            }
        }
    }

    pub fn get_or_request_image_or_meta(&self, url: Url, use_placeholder: UsePlaceholder)
                                -> Option<ImageOrMetadataAvailable> {
        // See if the image is already available
        let result = self.shared.image_cache_thread.find_image_or_metadata(url.clone(),
                                                                         use_placeholder);

        match result {
            Ok(image_or_metadata) => Some(image_or_metadata),
            Err(state) => {
                // If we are emitting an output file, then we need to block on
                // image load or we risk emitting an output file missing the image.
                let is_sync = opts::get().output_file.is_some() ||
                              opts::get().exit_after_load;

                match (state, is_sync) {
                    // Image failed to load, so just return nothing
                    (ImageState::LoadError, _) => None,
                    // Not loaded, test mode - load the image synchronously
                    (_, true) => {
                        let (sync_tx, sync_rx) = ipc::channel().unwrap();
                        self.shared.image_cache_thread.request_image(url,
                                                                   ImageCacheChan(sync_tx),
                                                                   None);
                        match sync_rx.recv().unwrap().image_response {
                            ImageResponse::Loaded(image) |
                            ImageResponse::PlaceholderLoaded(image) =>
                                Some(ImageOrMetadataAvailable::ImageAvailable(image)),
                            ImageResponse::None | ImageResponse::MetadataLoaded(_) =>
                                None,
                        }
                    }
                    // Not yet requested, async mode - request image or metadata from the cache
                    (ImageState::NotRequested, false) => {
                        let sender = self.shared.image_cache_sender.lock().unwrap().clone();
                        self.shared.image_cache_thread.request_image_and_metadata(url, sender, None);
                        None
                    }
                    // Image has been requested, is still pending. Return no image
                    // for this paint loop. When the image loads it will trigger
                    // a reflow and/or repaint.
                    (ImageState::Pending, false) => None,
                }
            }
        }
    }

}
<commit_msg>Auto merge of #9754 - mbrubeck:img-metadata, r=jdm<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/. */

//! Data needed by the layout thread.

// for thread_local
#![allow(unsafe_code)]

use app_units::Au;
use canvas_traits::CanvasMsg;
use euclid::Rect;
use fnv::FnvHasher;
use gfx::font_cache_thread::FontCacheThread;
use gfx::font_context::FontContext;
use gfx_traits::LayerId;
use heapsize::HeapSizeOf;
use ipc_channel::ipc::{self, IpcSender};
use net_traits::image::base::Image;
use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheThread, ImageResponse, ImageState};
use net_traits::image_cache_thread::{ImageOrMetadataAvailable, UsePlaceholder};
use std::cell::{RefCell, RefMut};
use std::collections::HashMap;
use std::hash::BuildHasherDefault;
use std::rc::Rc;
use std::sync::mpsc::Sender;
use std::sync::{Arc, Mutex};
use style::context::{LocalStyleContext, StyleContext};
use style::matching::{ApplicableDeclarationsCache, StyleSharingCandidateCache};
use style::selector_impl::ServoSelectorImpl;
use style::servo::SharedStyleContext;
use url::Url;
use util::opts;

struct LocalLayoutContext {
    style_context: LocalStyleContext,
    font_context: RefCell<FontContext>,
}

impl HeapSizeOf for LocalLayoutContext {
    // FIXME(njn): measure other fields eventually.
    fn heap_size_of_children(&self) -> usize {
        self.font_context.heap_size_of_children()
    }
}

thread_local!(static LOCAL_CONTEXT_KEY: RefCell<Option<Rc<LocalLayoutContext>>> = RefCell::new(None));

pub fn heap_size_of_local_context() -> usize {
    LOCAL_CONTEXT_KEY.with(|r| {
        r.borrow().clone().map_or(0, |context| context.heap_size_of_children())
    })
}

// Keep this implementation in sync with the one in ports/geckolib/traversal.rs.
fn create_or_get_local_context(shared_layout_context: &SharedLayoutContext)
                               -> Rc<LocalLayoutContext> {
    LOCAL_CONTEXT_KEY.with(|r| {
        let mut r = r.borrow_mut();
        if let Some(context) = r.clone() {
            if shared_layout_context.style_context.screen_size_changed {
                context.style_context.applicable_declarations_cache.borrow_mut().evict_all();
            }
            context
        } else {
            let font_cache_thread = shared_layout_context.font_cache_thread.lock().unwrap().clone();
            let context = Rc::new(LocalLayoutContext {
                style_context: LocalStyleContext {
                    applicable_declarations_cache: RefCell::new(ApplicableDeclarationsCache::new()),
                    style_sharing_candidate_cache: RefCell::new(StyleSharingCandidateCache::new()),
                },
                font_context: RefCell::new(FontContext::new(font_cache_thread)),
            });
            *r = Some(context.clone());
            context
        }
    })
}

/// Layout information shared among all workers. This must be thread-safe.
pub struct SharedLayoutContext {
    /// Bits shared by the layout and style system.
    pub style_context: SharedStyleContext,

    /// The shared image cache thread.
    pub image_cache_thread: ImageCacheThread,

    /// A channel for the image cache to send responses to.
    pub image_cache_sender: Mutex<ImageCacheChan>,

    /// Interface to the font cache thread.
    pub font_cache_thread: Mutex<FontCacheThread>,

    /// The URL.
    pub url: Url,

    /// A channel to send canvas renderers to paint thread, in order to correctly paint the layers
    pub canvas_layers_sender: Mutex<Sender<(LayerId, IpcSender<CanvasMsg>)>>,

    /// The visible rects for each layer, as reported to us by the compositor.
    pub visible_rects: Arc<HashMap<LayerId, Rect<Au>, BuildHasherDefault<FnvHasher>>>,
}

pub struct LayoutContext<'a> {
    pub shared: &'a SharedLayoutContext,
    cached_local_layout_context: Rc<LocalLayoutContext>,
}

impl<'a> StyleContext<'a, ServoSelectorImpl> for LayoutContext<'a> {
    fn shared_context(&self) -> &'a SharedStyleContext {
        &self.shared.style_context
    }

    fn local_context(&self) -> &LocalStyleContext {
        &self.cached_local_layout_context.style_context
    }
}

impl<'a> LayoutContext<'a> {
    pub fn new(shared_layout_context: &'a SharedLayoutContext) -> LayoutContext<'a> {

        let local_context = create_or_get_local_context(shared_layout_context);

        LayoutContext {
            shared: shared_layout_context,
            cached_local_layout_context: local_context,
        }
    }

    #[inline(always)]
    pub fn font_context(&self) -> RefMut<FontContext> {
        self.cached_local_layout_context.font_context.borrow_mut()
    }

    pub fn get_or_request_image(&self, url: Url, use_placeholder: UsePlaceholder)
                                -> Option<Arc<Image>> {
        // See if the image is already available
        let result = self.shared.image_cache_thread.find_image(url.clone(),
                                                             use_placeholder);

        match result {
            Ok(image) => Some(image),
            Err(state) => {
                // If we are emitting an output file, then we need to block on
                // image load or we risk emitting an output file missing the image.
                let is_sync = opts::get().output_file.is_some() ||
                              opts::get().exit_after_load;

                match (state, is_sync) {
                    // Image failed to load, so just return nothing
                    (ImageState::LoadError, _) => None,
                    // Not loaded, test mode - load the image synchronously
                    (_, true) => {
                        let (sync_tx, sync_rx) = ipc::channel().unwrap();
                        self.shared.image_cache_thread.request_image(url,
                                                                   ImageCacheChan(sync_tx),
                                                                   None);
                        match sync_rx.recv().unwrap().image_response {
                            ImageResponse::Loaded(image) |
                            ImageResponse::PlaceholderLoaded(image) => Some(image),
                            ImageResponse::None | ImageResponse::MetadataLoaded(_) => None,
                        }
                    }
                    // Not yet requested, async mode - request image from the cache
                    (ImageState::NotRequested, false) => {
                        let sender = self.shared.image_cache_sender.lock().unwrap().clone();
                        self.shared.image_cache_thread.request_image(url, sender, None);
                        None
                    }
                    // Image has been requested, is still pending. Return no image
                    // for this paint loop. When the image loads it will trigger
                    // a reflow and/or repaint.
                    (ImageState::Pending, false) => None,
                }
            }
        }
    }

    pub fn get_or_request_image_or_meta(&self, url: Url, use_placeholder: UsePlaceholder)
                                -> Option<ImageOrMetadataAvailable> {
        // If we are emitting an output file, load the image synchronously.
        if opts::get().output_file.is_some() || opts::get().exit_after_load {
            return self.get_or_request_image(url, use_placeholder)
                       .map(|img| ImageOrMetadataAvailable::ImageAvailable(img));
        }
        // See if the image is already available
        let result = self.shared.image_cache_thread.find_image_or_metadata(url.clone(),
                                                                           use_placeholder);
        match result {
            Ok(image_or_metadata) => Some(image_or_metadata),
            // Image failed to load, so just return nothing
            Err(ImageState::LoadError) => None,
            // Not yet requested, async mode - request image or metadata from the cache
            Err(ImageState::NotRequested) => {
                let sender = self.shared.image_cache_sender.lock().unwrap().clone();
                self.shared.image_cache_thread.request_image_and_metadata(url, sender, None);
                None
            }
            // Image has been requested, is still pending. Return no image for this paint loop.
            // When the image loads it will trigger a reflow and/or repaint.
            Err(ImageState::Pending) => None,
        }
    }

}
<|endoftext|>