<commit_before>//
//  ScrollView.cpp
//  moFlo
//
//  Created by Scott Downie on 27/04/2011.
//  Copyright 2011 Tag Games. All rights reserved.
//

#include <ChilliSource/GUI/Container/ScrollView.h>

#include <ChilliSource/Core/Math/MathUtils.h>
#include <ChilliSource/Core/String/StringParser.h>

#if CS_ENABLE_DEBUGDRAWING
#include <ChilliSource/Rendering/Base/CanvasRenderer.h>
#include <ChilliSource/Rendering/Texture/Texture.h>
#endif

namespace ChilliSource
{
    namespace GUI
    {
		DEFINE_META_CLASS(ScrollView)

		DEFINE_PROPERTY(ScrollHorizontally);
		DEFINE_PROPERTY(ScrollVertically);

        const f32 kfScrollDeceleration = 0.9f;

        //--------------------------------------------
        /// Constructor
        ///
        /// Default
        //--------------------------------------------
        ScrollView::ScrollView()
		: ScrollHorizontally(true), ScrollVertically(true), mpContainerView(new GUIView), mbTouchMoved(false), mbTouchActive(false), mfTouchTravel(0.0f),mbDrawDebug(false)
        {
            //A scroll view that doesn't clip is useless
            EnableSubviewClipping(true);

            //Lets give the scroll view an empty container that we can check bounds against
            //this container will expand to hold all it's children
            mpContainerView->SetSize(1.0f, 1.0f, 0.0f, 0.0f);
            mpContainerView->SetLocalAlignment(Rendering::AlignmentAnchor::k_topLeft);
            mpContainerView->EnableAlignmentToParent(true);
            mpContainerView->SetAlignmentToParent(Rendering::AlignmentAnchor::k_topLeft);
            mpContainerView->EnableTouchConsumption(false);
            GUIView::AddSubview(mpContainerView);
        }
        //--------------------------------------------
        /// Constructor
        ///
        /// From param dictionary
        //--------------------------------------------
        ScrollView::ScrollView(const Core::ParamDictionary& insParams)
		: GUIView(insParams), ScrollHorizontally(true), ScrollVertically(true), mpContainerView(new GUIView), mbTouchMoved(false), mbTouchActive(false), mfTouchTravel(0.0f),mbDrawDebug(false)
        {
            //A scroll view that doesn't clip is useless
            EnableSubviewClipping(true);

            std::string strValue;

            //---Enable Horizontal scrolling
            if(insParams.TryGetValue("ScrollHorizontally", strValue))
            {
                ScrollHorizontally = Core::ParseBool(strValue);
            }
            //---Enable Vertical scrolling
            if(insParams.TryGetValue("ScrollVertically", strValue))
            {
                ScrollVertically = Core::ParseBool(strValue);
            }

            //Lets give the scroll view an empty container that we can check bounds against
            //this container will expand to hold all it's children
            mpContainerView->SetSize(1.0f, 1.0f, 0.0f, 0.0f);
            mpContainerView->SetLocalAlignment(Rendering::AlignmentAnchor::k_topLeft);
            mpContainerView->EnableAlignmentToParent(true);
            mpContainerView->SetAlignmentToParent(Rendering::AlignmentAnchor::k_topLeft);
            mpContainerView->EnableTouchConsumption(false);
            GUIView::AddSubview(mpContainerView);
        }
        //-----------------------------------------------------
        /// Add Subview
        ///
        /// Add a view to the hierarchy
        ///
        /// @param GUIView shared pointer
        //-----------------------------------------------------
        void ScrollView::AddSubview(const GUIViewSPtr& inpSubview)
        {
            mpContainerView->AddSubview(inpSubview);
        }
        //-----------------------------------------------------
        /// Remove Subview (Internal)
        ///
        /// Remove a view from our hierarchy
        ///
        /// @param GUIView pointer
        //-----------------------------------------------------
        void ScrollView::RemoveSubview(GUIView* inpSubview)
        {
            mpContainerView->RemoveSubview(inpSubview);
        }
        //-----------------------------------------------------------
        /// Enable Horizontal Scrolling
        ///
        /// @param Whether the scroll view allows sideways scrolling
        //-----------------------------------------------------------
        void ScrollView::EnableHorizontalScrolling(bool inbEnabled)
        {
            ScrollHorizontally = inbEnabled;
        }
        //-----------------------------------------------------------
        /// Enable Vertical Scrolling
        ///
        /// @param Whether the scroll view allows vertical scrolling
        //-----------------------------------------------------------
        void ScrollView::EnableVerticalScrolling(bool inbEnabled)
        {
            ScrollVertically = inbEnabled;
        }
		//-----------------------------------------------------------
		/// Is Horizontal Scrolling Enabled
		///
		/// @return Whether the scroll view allows sideways scrolling
		//-----------------------------------------------------------
		bool ScrollView::IsHorizontalScrollingEnabled() const
		{
			return ScrollHorizontally;
		}
		//-----------------------------------------------------------
		/// Is Vertical Scrolling Enabled
		///
		/// @return Whether the scroll view allows vertical scrolling
		//-----------------------------------------------------------
		bool ScrollView::IsVerticalScrollingEnabled() const
		{
			return ScrollVertically;
		}
        //-----------------------------------------------------
        /// Update
        ///
        /// @param Time between frames
        //-----------------------------------------------------
        void ScrollView::Update(f32 infDt)
        {
            if(Visible)
            {
                //Check if the container exceeds the bounds of the scroll view
				//Get edge positions
				Core::Vector2 vTopLeft = GetAbsoluteScreenSpaceAnchorPoint(Rendering::AlignmentAnchor::k_topLeft);
				Core::Vector2 vBottomRight = GetAbsoluteScreenSpaceAnchorPoint(Rendering::AlignmentAnchor::k_bottomRight);

				//We don't want the scrollable items to fly into oblivion we must cap them.
				//The objects can only move in a direction until the furthest object in that direction is within the scroll view
				//at this point we "bounce" the objects.

				Core::Vector2 vNewLeftPosition = mvVelocity + mpContainerView->GetAbsoluteScreenSpaceAnchorPoint(Rendering::AlignmentAnchor::k_topLeft);
				Core::Vector2 vNewRightPosition = mvVelocity + mpContainerView->GetAbsoluteScreenSpaceAnchorPoint(Rendering::AlignmentAnchor::k_bottomRight);

				Core::Vector2 vSizeOfContainer = mpContainerView->GetAbsoluteScreenSpaceAnchorPoint(Rendering::AlignmentAnchor::k_topRight) - mpContainerView->GetAbsoluteScreenSpaceAnchorPoint(Rendering::AlignmentAnchor::k_bottomLeft);

				if(vSizeOfContainer.x > vBottomRight.x - vTopLeft.x)
				{
					// AM: Make sure we're not going to fly past the left edge
					if(vNewLeftPosition.x >= vTopLeft.x)
					{
						mvVelocity.x = vTopLeft.x - mpContainerView->GetAbsoluteScreenSpaceAnchorPoint(Rendering::AlignmentAnchor::k_topLeft).x;
					}
                    // AM: Make sure we're not going to fly past the right edge
					else if(vNewRightPosition.x <= vBottomRight.x)
                    {
                        mvVelocity.x = vBottomRight.x - mpContainerView->GetAbsoluteScreenSpaceAnchorPoint(Rendering::AlignmentAnchor::k_bottomRight).x;
                    }
				}
				else
                {
					mvVelocity.x = 0;
                }

				if(vSizeOfContainer.y > vTopLeft.y - vBottomRight.y)
				{
					// AM: Make sure we're not going to fly past the top edge
					if(vNewLeftPosition.y <= vTopLeft.y)
					{
						mvVelocity.y = vTopLeft.y - mpContainerView->GetAbsoluteScreenSpaceAnchorPoint(Rendering::AlignmentAnchor::k_topLeft).y;
					}
                    // AM: Make sure we're not going to fly past the bottom edge
					else if(vNewRightPosition.y >= vBottomRight.y)
                    {
                        mvVelocity.y = vBottomRight.y - mpContainerView->GetAbsoluteScreenSpaceAnchorPoint(Rendering::AlignmentAnchor::k_bottomRight).y;
                    }
				}
				else
                {
					mvVelocity.y = 0;
				}

                mpContainerView->MoveBy(0.0f, 0.0f, mvVelocity.x, mvVelocity.y);
				//Decelaration
				if(mbTouchActive && !mbTouchMoved)
				{
					mvVelocity = Core::Vector2::k_zero;
				}
				else if(!mbTouchActive)
				{
					mvVelocity *= kfScrollDeceleration;
				}
				if(mbTouchMoved)
				{
					mvRealPreviousTouchPosition = mvNextRealPreviousTouchPosition;
					mbTouchMoved = false;
				}

                GUIView::Update(infDt);
			}
        }
        //-----------------------------------------------------
        /// Reset
        ///
        /// Resets the scroller back to the default
        //-----------------------------------------------------
        void ScrollView::Reset()
        {
            mvVelocity = Core::Vector2::k_zero;
            mpContainerView->SetOffsetFromParentAlignment(Core::UnifiedVector2(Core::Vector2::k_zero, Core::Vector2::k_zero));
        }
        //-----------------------------------------------------
        /// Jump To
        ///
        /// Jumps to the given position
        ///
        /// @param The new position
        //-----------------------------------------------------
        void ScrollView::JumpTo(const Core::UnifiedVector2& inuvPosition)
        {
            Reset();
            Core::Vector2 vAbsPos = inuvPosition.GetAbsolute() + (inuvPosition.GetRelative()*mpContainerView->GetAbsoluteSize());
            mpContainerView->MoveBy(0.0f, 0.0f, vAbsPos.x, vAbsPos.y);
        }
        //-----------------------------------------------------
        /// Set Absolute Content Size
        ///
        /// @param Content size
        //-----------------------------------------------------
        void ScrollView::SetAbsoluteContentSize(const Core::Vector2& invSize)
        {
            mpContainerView->SetSize(0.0f, 0.0f, invSize.x, invSize.y);
        }
        //-----------------------------------------------------
        /// Get Absolute Content Size
        ///
        /// @return Content size
        //-----------------------------------------------------
        Core::Vector2 ScrollView::GetAbsoluteContentSize() const
        {
            if(!mpContainerView)
                return Core::Vector2::k_zero;
            return mpContainerView->GetAbsoluteSize();
        }
        //-----------------------------------------------------------
        /// Get Absolute Content Position
        ///
        /// @return The current absolute position of the content
        /// from the top left corner of the scroll view
        //-----------------------------------------------------------
        Core::Vector2 ScrollView::GetAbsoluteContentPosition() const
        {
            if(!mpContainerView)
                return Core::Vector2::k_zero;
            return mpContainerView->GetAbsoluteScreenSpaceAnchorPoint(Rendering::AlignmentAnchor::k_topLeft) - GetAbsoluteScreenSpaceAnchorPoint(Rendering::AlignmentAnchor::k_topLeft);
        }
        //-----------------------------------------------------
        /// Set Velocity
        ///
        /// @param Velocity
        //-----------------------------------------------------
        void ScrollView::SetVelocity(const Core::Vector2& invVelocity)
        {
            mvVelocity = invVelocity;
        }
        //-----------------------------------------------------------
        //-----------------------------------------------------------
        bool ScrollView::OnPointerDown(const Input::PointerSystem::Pointer& in_pointer, f64 in_timestamp, Input::PointerSystem::InputType in_inputType)
        {
            if(UserInteraction && Visible)
            {
				mbTouchActive = true;
				mvRealPreviousTouchPosition = in_pointer.m_location;

                //Stop! Hammer time...
                mvVelocity = Core::Vector2::k_zero;
				mfTouchTravel = 0.0f;
            }

            return GUIView::OnPointerDown(in_pointer, in_timestamp, in_inputType);
        }
        //-----------------------------------------------------------
        //-----------------------------------------------------------
        bool ScrollView::OnPointerMoved(const Input::PointerSystem::Pointer& in_pointer, f64 in_timestamp)
        {
            if(UserInteraction && Visible && mbTouchActive && Contains(in_pointer.m_location))
            {
                //Calculate the displacement
                mvVelocity = in_pointer.m_location - mvRealPreviousTouchPosition;
				mfTouchTravel += mvVelocity.LengthSquared();

                if(!ScrollHorizontally)
                {
                    mvVelocity.x = 0.0f;
                }
                if(!ScrollVertically)
                {
                    mvVelocity.y = 0.0f;
                }

				mvNextRealPreviousTouchPosition = in_pointer.m_location;
				mbTouchMoved = true;

				GUIView::OnPointerMoved(in_pointer, in_timestamp);
            }

            return false;
        }
		//-----------------------------------------------------------
        //-----------------------------------------------------------
        void ScrollView::OnPointerUp(const Input::PointerSystem::Pointer& in_pointer, f64 in_timestamp, Input::PointerSystem::InputType in_inputType)
        {
			if(UserInteraction && Visible)
			{
				mbTouchActive = false;
			}

            GUIView::OnPointerUp(in_pointer, in_timestamp, in_inputType);
        }
        //-------------------------------------------------------
        /// Draw
        ///
        /// Draw all our subviews in neat rows and columns. Each
        /// cell is based on the size of the largest content
        ///
        /// @param Canvas renderer pointer
        //-------------------------------------------------------
        void ScrollView::Draw(Rendering::CanvasRenderer* inpCanvas)
        {
#if CS_ENABLE_DEBUGDRAWING
            if(mbDrawDebug)
            {
                Rendering::TextureManager* pMgr = (Rendering::TextureManager*)(Core::ResourceManagerDispenser::GetSingletonPtr()->GetResourceManagerForType(Rendering::Texture::InterfaceID));
                inpCanvas->DrawBox(GetTransform(), GetAbsoluteSize(), pMgr->GetDefaultTexture(), Core::Rectangle(Core::Vector2::k_zero, Core::Vector2::k_zero), Core::Colour(1.0f,0.0f,0.0f,0.5f));
            }
#endif

            GUIView::Draw(inpCanvas);
        }
		//-------------------------------------------------------
		/// Sets Debug Drawing
		///
		/// Enables/Disables debug drawing
		///
		/// @param New value for this flag. DEBUG_DRAWING must be
		/// set to TRUE
		//-------------------------------------------------------
		void ScrollView::EnableDebugDrawing(bool inbValue)
		{
#if CS_ENABLE_DEBUGDRAWING
			mbDrawDebug = inbValue;
#else
			mbDrawDebug = false;
#endif
		}
        //-----------------------------------------------------------
        /// Get Container View
        ///
        /// @return The GUIView that contains all scrollable subviews
        /// within the scroll view.
        //-----------------------------------------------------------
        const GUIViewSPtr& ScrollView::GetContainerView() const
        {
            return mpContainerView;
        }
    }
}
<commit_msg>Scroll view fix for move not accepting touches outside of bounds<commit_after>//
//  ScrollView.cpp
//  moFlo
//
//  Created by Scott Downie on 27/04/2011.
//  Copyright 2011 Tag Games. All rights reserved.
//

#include <ChilliSource/GUI/Container/ScrollView.h>

#include <ChilliSource/Core/Math/MathUtils.h>
#include <ChilliSource/Core/String/StringParser.h>

#if CS_ENABLE_DEBUGDRAWING
#include <ChilliSource/Rendering/Base/CanvasRenderer.h>
#include <ChilliSource/Rendering/Texture/Texture.h>
#endif

namespace ChilliSource
{
    namespace GUI
    {
		DEFINE_META_CLASS(ScrollView)

		DEFINE_PROPERTY(ScrollHorizontally);
		DEFINE_PROPERTY(ScrollVertically);

        const f32 kfScrollDeceleration = 0.9f;

        //--------------------------------------------
        /// Constructor
        ///
        /// Default
        //--------------------------------------------
        ScrollView::ScrollView()
		: ScrollHorizontally(true), ScrollVertically(true), mpContainerView(new GUIView), mbTouchMoved(false), mbTouchActive(false), mfTouchTravel(0.0f),mbDrawDebug(false)
        {
            //A scroll view that doesn't clip is useless
            EnableSubviewClipping(true);

            //Lets give the scroll view an empty container that we can check bounds against
            //this container will expand to hold all it's children
            mpContainerView->SetSize(1.0f, 1.0f, 0.0f, 0.0f);
            mpContainerView->SetLocalAlignment(Rendering::AlignmentAnchor::k_topLeft);
            mpContainerView->EnableAlignmentToParent(true);
            mpContainerView->SetAlignmentToParent(Rendering::AlignmentAnchor::k_topLeft);
            mpContainerView->EnableTouchConsumption(false);
            GUIView::AddSubview(mpContainerView);
        }
        //--------------------------------------------
        /// Constructor
        ///
        /// From param dictionary
        //--------------------------------------------
        ScrollView::ScrollView(const Core::ParamDictionary& insParams)
		: GUIView(insParams), ScrollHorizontally(true), ScrollVertically(true), mpContainerView(new GUIView), mbTouchMoved(false), mbTouchActive(false), mfTouchTravel(0.0f),mbDrawDebug(false)
        {
            //A scroll view that doesn't clip is useless
            EnableSubviewClipping(true);

            std::string strValue;

            //---Enable Horizontal scrolling
            if(insParams.TryGetValue("ScrollHorizontally", strValue))
            {
                ScrollHorizontally = Core::ParseBool(strValue);
            }
            //---Enable Vertical scrolling
            if(insParams.TryGetValue("ScrollVertically", strValue))
            {
                ScrollVertically = Core::ParseBool(strValue);
            }

            //Lets give the scroll view an empty container that we can check bounds against
            //this container will expand to hold all it's children
            mpContainerView->SetSize(1.0f, 1.0f, 0.0f, 0.0f);
            mpContainerView->SetLocalAlignment(Rendering::AlignmentAnchor::k_topLeft);
            mpContainerView->EnableAlignmentToParent(true);
            mpContainerView->SetAlignmentToParent(Rendering::AlignmentAnchor::k_topLeft);
            mpContainerView->EnableTouchConsumption(false);
            GUIView::AddSubview(mpContainerView);
        }
        //-----------------------------------------------------
        /// Add Subview
        ///
        /// Add a view to the hierarchy
        ///
        /// @param GUIView shared pointer
        //-----------------------------------------------------
        void ScrollView::AddSubview(const GUIViewSPtr& inpSubview)
        {
            mpContainerView->AddSubview(inpSubview);
        }
        //-----------------------------------------------------
        /// Remove Subview (Internal)
        ///
        /// Remove a view from our hierarchy
        ///
        /// @param GUIView pointer
        //-----------------------------------------------------
        void ScrollView::RemoveSubview(GUIView* inpSubview)
        {
            mpContainerView->RemoveSubview(inpSubview);
        }
        //-----------------------------------------------------------
        /// Enable Horizontal Scrolling
        ///
        /// @param Whether the scroll view allows sideways scrolling
        //-----------------------------------------------------------
        void ScrollView::EnableHorizontalScrolling(bool inbEnabled)
        {
            ScrollHorizontally = inbEnabled;
        }
        //-----------------------------------------------------------
        /// Enable Vertical Scrolling
        ///
        /// @param Whether the scroll view allows vertical scrolling
        //-----------------------------------------------------------
        void ScrollView::EnableVerticalScrolling(bool inbEnabled)
        {
            ScrollVertically = inbEnabled;
        }
		//-----------------------------------------------------------
		/// Is Horizontal Scrolling Enabled
		///
		/// @return Whether the scroll view allows sideways scrolling
		//-----------------------------------------------------------
		bool ScrollView::IsHorizontalScrollingEnabled() const
		{
			return ScrollHorizontally;
		}
		//-----------------------------------------------------------
		/// Is Vertical Scrolling Enabled
		///
		/// @return Whether the scroll view allows vertical scrolling
		//-----------------------------------------------------------
		bool ScrollView::IsVerticalScrollingEnabled() const
		{
			return ScrollVertically;
		}
        //-----------------------------------------------------
        /// Update
        ///
        /// @param Time between frames
        //-----------------------------------------------------
        void ScrollView::Update(f32 infDt)
        {
            if(Visible)
            {
                //Check if the container exceeds the bounds of the scroll view
				//Get edge positions
				Core::Vector2 vTopLeft = GetAbsoluteScreenSpaceAnchorPoint(Rendering::AlignmentAnchor::k_topLeft);
				Core::Vector2 vBottomRight = GetAbsoluteScreenSpaceAnchorPoint(Rendering::AlignmentAnchor::k_bottomRight);

				//We don't want the scrollable items to fly into oblivion we must cap them.
				//The objects can only move in a direction until the furthest object in that direction is within the scroll view
				//at this point we "bounce" the objects.

				Core::Vector2 vNewLeftPosition = mvVelocity + mpContainerView->GetAbsoluteScreenSpaceAnchorPoint(Rendering::AlignmentAnchor::k_topLeft);
				Core::Vector2 vNewRightPosition = mvVelocity + mpContainerView->GetAbsoluteScreenSpaceAnchorPoint(Rendering::AlignmentAnchor::k_bottomRight);

				Core::Vector2 vSizeOfContainer = mpContainerView->GetAbsoluteScreenSpaceAnchorPoint(Rendering::AlignmentAnchor::k_topRight) - mpContainerView->GetAbsoluteScreenSpaceAnchorPoint(Rendering::AlignmentAnchor::k_bottomLeft);

				if(vSizeOfContainer.x > vBottomRight.x - vTopLeft.x)
				{
					// AM: Make sure we're not going to fly past the left edge
					if(vNewLeftPosition.x >= vTopLeft.x)
					{
						mvVelocity.x = vTopLeft.x - mpContainerView->GetAbsoluteScreenSpaceAnchorPoint(Rendering::AlignmentAnchor::k_topLeft).x;
					}
                    // AM: Make sure we're not going to fly past the right edge
					else if(vNewRightPosition.x <= vBottomRight.x)
                    {
                        mvVelocity.x = vBottomRight.x - mpContainerView->GetAbsoluteScreenSpaceAnchorPoint(Rendering::AlignmentAnchor::k_bottomRight).x;
                    }
				}
				else
                {
					mvVelocity.x = 0;
                }

				if(vSizeOfContainer.y > vTopLeft.y - vBottomRight.y)
				{
					// AM: Make sure we're not going to fly past the top edge
					if(vNewLeftPosition.y <= vTopLeft.y)
					{
						mvVelocity.y = vTopLeft.y - mpContainerView->GetAbsoluteScreenSpaceAnchorPoint(Rendering::AlignmentAnchor::k_topLeft).y;
					}
                    // AM: Make sure we're not going to fly past the bottom edge
					else if(vNewRightPosition.y >= vBottomRight.y)
                    {
                        mvVelocity.y = vBottomRight.y - mpContainerView->GetAbsoluteScreenSpaceAnchorPoint(Rendering::AlignmentAnchor::k_bottomRight).y;
                    }
				}
				else
                {
					mvVelocity.y = 0;
				}

                mpContainerView->MoveBy(0.0f, 0.0f, mvVelocity.x, mvVelocity.y);
				//Decelaration
				if(mbTouchActive && !mbTouchMoved)
				{
					mvVelocity = Core::Vector2::k_zero;
				}
				else if(!mbTouchActive)
				{
					mvVelocity *= kfScrollDeceleration;
				}
				if(mbTouchMoved)
				{
					mvRealPreviousTouchPosition = mvNextRealPreviousTouchPosition;
					mbTouchMoved = false;
				}

                GUIView::Update(infDt);
			}
        }
        //-----------------------------------------------------
        /// Reset
        ///
        /// Resets the scroller back to the default
        //-----------------------------------------------------
        void ScrollView::Reset()
        {
            mvVelocity = Core::Vector2::k_zero;
            mpContainerView->SetOffsetFromParentAlignment(Core::UnifiedVector2(Core::Vector2::k_zero, Core::Vector2::k_zero));
        }
        //-----------------------------------------------------
        /// Jump To
        ///
        /// Jumps to the given position
        ///
        /// @param The new position
        //-----------------------------------------------------
        void ScrollView::JumpTo(const Core::UnifiedVector2& inuvPosition)
        {
            Reset();
            Core::Vector2 vAbsPos = inuvPosition.GetAbsolute() + (inuvPosition.GetRelative()*mpContainerView->GetAbsoluteSize());
            mpContainerView->MoveBy(0.0f, 0.0f, vAbsPos.x, vAbsPos.y);
        }
        //-----------------------------------------------------
        /// Set Absolute Content Size
        ///
        /// @param Content size
        //-----------------------------------------------------
        void ScrollView::SetAbsoluteContentSize(const Core::Vector2& invSize)
        {
            mpContainerView->SetSize(0.0f, 0.0f, invSize.x, invSize.y);
        }
        //-----------------------------------------------------
        /// Get Absolute Content Size
        ///
        /// @return Content size
        //-----------------------------------------------------
        Core::Vector2 ScrollView::GetAbsoluteContentSize() const
        {
            if(!mpContainerView)
                return Core::Vector2::k_zero;
            return mpContainerView->GetAbsoluteSize();
        }
        //-----------------------------------------------------------
        /// Get Absolute Content Position
        ///
        /// @return The current absolute position of the content
        /// from the top left corner of the scroll view
        //-----------------------------------------------------------
        Core::Vector2 ScrollView::GetAbsoluteContentPosition() const
        {
            if(!mpContainerView)
                return Core::Vector2::k_zero;
            return mpContainerView->GetAbsoluteScreenSpaceAnchorPoint(Rendering::AlignmentAnchor::k_topLeft) - GetAbsoluteScreenSpaceAnchorPoint(Rendering::AlignmentAnchor::k_topLeft);
        }
        //-----------------------------------------------------
        /// Set Velocity
        ///
        /// @param Velocity
        //-----------------------------------------------------
        void ScrollView::SetVelocity(const Core::Vector2& invVelocity)
        {
            mvVelocity = invVelocity;
        }
        //-----------------------------------------------------------
        //-----------------------------------------------------------
        bool ScrollView::OnPointerDown(const Input::PointerSystem::Pointer& in_pointer, f64 in_timestamp, Input::PointerSystem::InputType in_inputType)
        {
            if(UserInteraction && Visible)
            {
				mbTouchActive = true;
				mvRealPreviousTouchPosition = in_pointer.m_location;

                //Stop! Hammer time...
                mvVelocity = Core::Vector2::k_zero;
				mfTouchTravel = 0.0f;
            }

            return GUIView::OnPointerDown(in_pointer, in_timestamp, in_inputType);
        }
        //-----------------------------------------------------------
        //-----------------------------------------------------------
        bool ScrollView::OnPointerMoved(const Input::PointerSystem::Pointer& in_pointer, f64 in_timestamp)
        {
            if(UserInteraction && Visible && mbTouchActive && (Contains(in_pointer.m_location) == true || IsAcceptTouchesOutsideOfBoundsEnabled() == true))
            {
                //Calculate the displacement
                mvVelocity = in_pointer.m_location - mvRealPreviousTouchPosition;
				mfTouchTravel += mvVelocity.LengthSquared();

                if(!ScrollHorizontally)
                {
                    mvVelocity.x = 0.0f;
                }
                if(!ScrollVertically)
                {
                    mvVelocity.y = 0.0f;
                }

				mvNextRealPreviousTouchPosition = in_pointer.m_location;
				mbTouchMoved = true;

				GUIView::OnPointerMoved(in_pointer, in_timestamp);
            }

            return false;
        }
		//-----------------------------------------------------------
        //-----------------------------------------------------------
        void ScrollView::OnPointerUp(const Input::PointerSystem::Pointer& in_pointer, f64 in_timestamp, Input::PointerSystem::InputType in_inputType)
        {
			if(UserInteraction && Visible)
			{
				mbTouchActive = false;
			}

            GUIView::OnPointerUp(in_pointer, in_timestamp, in_inputType);
        }
        //-------------------------------------------------------
        /// Draw
        ///
        /// Draw all our subviews in neat rows and columns. Each
        /// cell is based on the size of the largest content
        ///
        /// @param Canvas renderer pointer
        //-------------------------------------------------------
        void ScrollView::Draw(Rendering::CanvasRenderer* inpCanvas)
        {
#if CS_ENABLE_DEBUGDRAWING
            if(mbDrawDebug)
            {
                Rendering::TextureManager* pMgr = (Rendering::TextureManager*)(Core::ResourceManagerDispenser::GetSingletonPtr()->GetResourceManagerForType(Rendering::Texture::InterfaceID));
                inpCanvas->DrawBox(GetTransform(), GetAbsoluteSize(), pMgr->GetDefaultTexture(), Core::Rectangle(Core::Vector2::k_zero, Core::Vector2::k_zero), Core::Colour(1.0f,0.0f,0.0f,0.5f));
            }
#endif

            GUIView::Draw(inpCanvas);
        }
		//-------------------------------------------------------
		/// Sets Debug Drawing
		///
		/// Enables/Disables debug drawing
		///
		/// @param New value for this flag. DEBUG_DRAWING must be
		/// set to TRUE
		//-------------------------------------------------------
		void ScrollView::EnableDebugDrawing(bool inbValue)
		{
#if CS_ENABLE_DEBUGDRAWING
			mbDrawDebug = inbValue;
#else
			mbDrawDebug = false;
#endif
		}
        //-----------------------------------------------------------
        /// Get Container View
        ///
        /// @return The GUIView that contains all scrollable subviews
        /// within the scroll view.
        //-----------------------------------------------------------
        const GUIViewSPtr& ScrollView::GetContainerView() const
        {
            return mpContainerView;
        }
    }
}
<|endoftext|>


<commit_before>/******************************************************************************

  This source file is part of the Delaunay project.

  Copyright T.J. Corona

  This source code is released under the New BSD License, (the "License").

  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.

******************************************************************************/

#include "ConstrainedDelaunayMesh.hh"

#include "Discretization/InsertLineSegment.hh"
#include "Discretization/RemoveBoundedRegion.hh"
#include "Shape/PolygonUtilities.hh"

#include <limits>

namespace Delaunay
{
namespace Discretization
{
void ConstrainedDelaunayMesh::operator()(
  const Delaunay::Shape::Polygon& polygon, Delaunay::Mesh::Mesh& mesh)
{
  std::array<double, 4> bounds(Shape::Bounds(polygon));
  double xLen = bounds[1] - bounds[0];
  double yLen = bounds[3] - bounds[2];
  bounds[0] -= .2*xLen;
  bounds[1] += .2*xLen;
  bounds[2] -= .2*yLen;
  bounds[3] += .2*yLen;

  bool inSitu = mesh.GetVertices().empty();

  Mesh::Mesh mesh_;
  Mesh::Mesh* augmentedMesh;
  if (inSitu)
    augmentedMesh = &mesh;
  else
    augmentedMesh = &mesh_;

  const Mesh::Vertex& v0 = *(this->InsertVertex(
                               Shape::Point(bounds[0],bounds[2]),
                               *augmentedMesh)).first;
  const Mesh::Vertex& v1 = *(this->InsertVertex(
                               Shape::Point(bounds[1],bounds[2]),
                               *augmentedMesh)).first;
  const Mesh::Vertex& v2 = *(this->InsertVertex(
                               Shape::Point(bounds[0],bounds[3]),
                               *augmentedMesh)).first;
  const Mesh::Vertex& v3 = *(this->InsertVertex(
                               Shape::Point(bounds[1],bounds[3]),
                               *augmentedMesh)).first;

  const Mesh::Edge& e01 =*(this->InsertEdge(v0,v1,*augmentedMesh)).first;
  const Mesh::Edge& e02 =*(this->InsertEdge(v0,v2,*augmentedMesh)).first;
  const Mesh::Edge& e12 =*(this->InsertEdge(v1,v2,*augmentedMesh)).first;
  const Mesh::Edge& e13 =*(this->InsertEdge(v1,v3,*augmentedMesh)).first;
  const Mesh::Edge& e23 =*(this->InsertEdge(v2,v3,*augmentedMesh)).first;

  this->GetTriangles(*augmentedMesh).emplace(e01,e02,e12);
  this->GetTriangles(*augmentedMesh).emplace(e13,e23,e12);

  InsertLineSegment insertLineSegment;

  Mesh::VertexList list;
  std::pair<const Mesh::Edge*, bool> firstEdge;
  for (Shape::PointList::const_iterator it = polygon.GetPoints().begin();
       it != polygon.GetPoints().end(); ++it)
  {
    Shape::PointList::const_iterator next = std::next(it);
    if (next == polygon.GetPoints().end())
      next = polygon.GetPoints().begin();
    Shape::LineSegment l(*it, *next);
    const Mesh::Edge* edge = insertLineSegment(l, mesh);
    list.push_back(std::cref(edge->A() == *it ? edge->A() : edge->B()));
    if (it == polygon.GetPoints().begin())
    {
      bool isCCW =
	Shape::Dot(edge->B() - edge->A(), *next - *it) > 0.;
      firstEdge = std::make_pair(edge, isCCW);
    }
  }

  RemoveBoundedRegion removeBoundedRegion;
  removeBoundedRegion(*firstEdge.first, !firstEdge.second, *augmentedMesh);

  if (inSitu)
  {
    this->GetPerimeter(mesh).SetVertices(list);
  }
  else
  {
    RemoveBoundedRegion removeBoundedRegion;
    removeBoundedRegion(*firstEdge.first, !firstEdge.second, *augmentedMesh);

    if (inSitu)
    {
      this->GetPerimeter(mesh).SetPoints(vec);
    }
    else
    {
      for (auto& triangle : mesh_.GetTriangles())
      {
        const Mesh::Vertex& a = *(this->InsertVertex(
                                    triangle.AB().A(),mesh)).first;
        const Mesh::Vertex& b = *(this->InsertVertex(
                                    triangle.AB().B(),mesh)).first;
        const Mesh::Vertex& c = *(this->InsertVertex(
                                    triangle.AC().B(),mesh)).first;
        const Mesh::Edge& ab = *(this->InsertEdge(a,b,mesh)).first;
        const Mesh::Edge& bc = *(this->InsertEdge(b,c,mesh)).first;
        const Mesh::Edge& ac = *(this->InsertEdge(a,c,mesh)).first;
        this->InsertTriangle(ab,bc,ac,mesh);
      }
    }
  }
}

}
}
<commit_msg>Fix merge bug<commit_after>/******************************************************************************

  This source file is part of the Delaunay project.

  Copyright T.J. Corona

  This source code is released under the New BSD License, (the "License").

  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.

******************************************************************************/

#include "ConstrainedDelaunayMesh.hh"

#include "Discretization/InsertLineSegment.hh"
#include "Discretization/RemoveBoundedRegion.hh"
#include "Shape/PolygonUtilities.hh"

#include <limits>

namespace Delaunay
{
namespace Discretization
{
void ConstrainedDelaunayMesh::operator()(
  const Delaunay::Shape::Polygon& polygon, Delaunay::Mesh::Mesh& mesh)
{
  std::array<double, 4> bounds(Shape::Bounds(polygon));
  double xLen = bounds[1] - bounds[0];
  double yLen = bounds[3] - bounds[2];
  bounds[0] -= .2*xLen;
  bounds[1] += .2*xLen;
  bounds[2] -= .2*yLen;
  bounds[3] += .2*yLen;

  bool inSitu = mesh.GetVertices().empty();

  Mesh::Mesh mesh_;
  Mesh::Mesh* augmentedMesh;
  if (inSitu)
    augmentedMesh = &mesh;
  else
    augmentedMesh = &mesh_;

  const Mesh::Vertex& v0 = *(this->InsertVertex(
                               Shape::Point(bounds[0],bounds[2]),
                               *augmentedMesh)).first;
  const Mesh::Vertex& v1 = *(this->InsertVertex(
                               Shape::Point(bounds[1],bounds[2]),
                               *augmentedMesh)).first;
  const Mesh::Vertex& v2 = *(this->InsertVertex(
                               Shape::Point(bounds[0],bounds[3]),
                               *augmentedMesh)).first;
  const Mesh::Vertex& v3 = *(this->InsertVertex(
                               Shape::Point(bounds[1],bounds[3]),
                               *augmentedMesh)).first;

  const Mesh::Edge& e01 =*(this->InsertEdge(v0,v1,*augmentedMesh)).first;
  const Mesh::Edge& e02 =*(this->InsertEdge(v0,v2,*augmentedMesh)).first;
  const Mesh::Edge& e12 =*(this->InsertEdge(v1,v2,*augmentedMesh)).first;
  const Mesh::Edge& e13 =*(this->InsertEdge(v1,v3,*augmentedMesh)).first;
  const Mesh::Edge& e23 =*(this->InsertEdge(v2,v3,*augmentedMesh)).first;

  this->GetTriangles(*augmentedMesh).emplace(e01,e02,e12);
  this->GetTriangles(*augmentedMesh).emplace(e13,e23,e12);

  InsertLineSegment insertLineSegment;

  Mesh::VertexList list;
  std::pair<const Mesh::Edge*, bool> firstEdge;
  for (Shape::PointList::const_iterator it = polygon.GetPoints().begin();
       it != polygon.GetPoints().end(); ++it)
  {
    Shape::PointList::const_iterator next = std::next(it);
    if (next == polygon.GetPoints().end())
      next = polygon.GetPoints().begin();
    Shape::LineSegment l(*it, *next);
    const Mesh::Edge* edge = insertLineSegment(l, mesh);
    list.push_back(std::cref(edge->A() == *it ? edge->A() : edge->B()));
    if (it == polygon.GetPoints().begin())
    {
      bool isCCW =
	Shape::Dot(edge->B() - edge->A(), *next - *it) > 0.;
      firstEdge = std::make_pair(edge, isCCW);
    }
  }

  RemoveBoundedRegion removeBoundedRegion;
  removeBoundedRegion(*firstEdge.first, !firstEdge.second, *augmentedMesh);

  if (inSitu)
  {
    this->GetPerimeter(mesh).SetVertices(list);
  }
  else
  {
    RemoveBoundedRegion removeBoundedRegion;
    removeBoundedRegion(*firstEdge.first, !firstEdge.second, *augmentedMesh);

    if (inSitu)
    {
      this->GetPerimeter(mesh).SetVertices(list);
    }
    else
    {
      for (auto& triangle : mesh_.GetTriangles())
      {
        const Mesh::Vertex& a = *(this->InsertVertex(
                                    triangle.AB().A(),mesh)).first;
        const Mesh::Vertex& b = *(this->InsertVertex(
                                    triangle.AB().B(),mesh)).first;
        const Mesh::Vertex& c = *(this->InsertVertex(
                                    triangle.AC().B(),mesh)).first;
        const Mesh::Edge& ab = *(this->InsertEdge(a,b,mesh)).first;
        const Mesh::Edge& bc = *(this->InsertEdge(b,c,mesh)).first;
        const Mesh::Edge& ac = *(this->InsertEdge(a,c,mesh)).first;
        this->InsertTriangle(ab,bc,ac,mesh);
      }
    }
  }
}

}
}
<|endoftext|>


<commit_before>/*

Copyright (c) 2007, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#ifndef TORRENT_BANDWIDTH_MANAGER_HPP_INCLUDED
#define TORRENT_BANDWIDTH_MANAGER_HPP_INCLUDED

#include <boost/shared_ptr.hpp>
#include <boost/intrusive_ptr.hpp>
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <boost/integer_traits.hpp>
#include <deque>

#ifdef TORRENT_VERBOSE_BANDWIDTH_LIMIT
#include <fstream>
#endif

#include "libtorrent/socket.hpp"
#include "libtorrent/invariant_check.hpp"
#include "libtorrent/assert.hpp"
#include "libtorrent/bandwidth_limit.hpp"
#include "libtorrent/bandwidth_queue_entry.hpp"

using boost::weak_ptr;
using boost::shared_ptr;
using boost::intrusive_ptr;
using boost::bind;

namespace libtorrent {

template<class PeerConnection>
struct bandwidth_manager
{
	bandwidth_manager(int channel
#ifdef TORRENT_VERBOSE_BANDWIDTH_LIMIT
		, bool log = false
#endif
		)
		: m_queued_bytes(0)
		, m_channel(channel)
		, m_abort(false)
	{
#ifdef TORRENT_VERBOSE_BANDWIDTH_LIMIT
		if (log)
			m_log.open("bandwidth_limiter.log", std::ios::trunc);
		m_start = time_now();
#endif
	}

	void close()
	{
		m_abort = true;
		m_queue.clear();
		m_queued_bytes = 0;
		error_code ec;
	}

#ifdef TORRENT_DEBUG
	bool is_queued(PeerConnection const* peer) const
	{
		for (typename queue_t::const_iterator i = m_queue.begin()
			, end(m_queue.end()); i != end; ++i)
		{
			if (i->peer.get() == peer) return true;
		}
		return false;
	}
#endif

	int queue_size() const
	{
		return m_queue.size();
	}

	int queued_bytes() const
	{
		return m_queued_bytes;
	}

	// non prioritized means that, if there's a line for bandwidth,
	// others will cut in front of the non-prioritized peers.
	// this is used by web seeds
	void request_bandwidth(intrusive_ptr<PeerConnection> const& peer
		, int blk, int priority
		, bandwidth_channel* chan1 = 0
		, bandwidth_channel* chan2 = 0
		, bandwidth_channel* chan3 = 0
		, bandwidth_channel* chan4 = 0
		, bandwidth_channel* chan5 = 0
		)
	{
		INVARIANT_CHECK;
		if (m_abort) return;

		TORRENT_ASSERT(blk > 0);
		TORRENT_ASSERT(priority > 0);
		TORRENT_ASSERT(!is_queued(peer.get()));

		bw_request<PeerConnection> bwr(peer, blk, priority);
		int i = 0;
		if (chan1 && chan1->throttle() > 0) bwr.channel[i++] = chan1;
		if (chan2 && chan2->throttle() > 0) bwr.channel[i++] = chan2;
		if (chan3 && chan3->throttle() > 0) bwr.channel[i++] = chan3;
		if (chan4 && chan4->throttle() > 0) bwr.channel[i++] = chan4;
		if (chan5 && chan5->throttle() > 0) bwr.channel[i++] = chan5;
		if (i == 0)
		{
			// the connection is not rate limited by any of its
			// bandwidth channels, or it doesn't belong to any
			// channels. There's no point in adding it to
			// the queue, just satisfy the request immediately
			bwr.peer->assign_bandwidth(m_channel, blk);
			return;
		}
		m_queued_bytes += blk;
		m_queue.push_back(bwr);
	}

#ifdef TORRENT_DEBUG
	void check_invariant() const
	{
	}
#endif

	void update_quotas(time_duration const& dt)
	{
		if (m_abort) return;
		if (m_queue.empty()) return;

		INVARIANT_CHECK;

		int dt_milliseconds = total_milliseconds(dt);
		if (dt_milliseconds > 3000) dt_milliseconds = 3000;

		// for each bandwidth channel, call update_quota(dt)

		std::vector<bandwidth_channel*> channels;

		for (typename queue_t::iterator i = m_queue.begin();
			i != m_queue.end();)
		{
			if (i->peer->is_disconnecting())
			{
				m_queued_bytes -= i->request_size - i->assigned;

				// return all assigned quota to all the
				// bandwidth channels this peer belongs to
				for (int j = 0; j < 5 && i->channel[j]; ++j)
				{
					bandwidth_channel* bwc = i->channel[j];
					bwc->return_quota(i->assigned);
				}

				i = m_queue.erase(i);
				continue;
			}
			for (int j = 0; j < 5 && i->channel[j]; ++j)
			{
				bandwidth_channel* bwc = i->channel[j];
				bwc->tmp = 0;
			}
			++i;
		}

		for (typename queue_t::iterator i = m_queue.begin()
			, end(m_queue.end()); i != end; ++i)
		{
			for (int j = 0; j < 5 && i->channel[j]; ++j)
			{
				bandwidth_channel* bwc = i->channel[j];
				if (bwc->tmp == 0) channels.push_back(bwc);
				bwc->tmp += i->priority;
				TORRENT_ASSERT(i->priority > 0);
			}
		}

		for (std::vector<bandwidth_channel*>::iterator i = channels.begin()
			, end(channels.end()); i != end; ++i)
		{
			(*i)->update_quota(dt_milliseconds);
		}

		queue_t tm;

		for (typename queue_t::iterator i = m_queue.begin();
			i != m_queue.end();)
		{
			int a = i->assign_bandwidth();
			if (i->assigned == i->request_size
				|| (i->ttl <= 0 && i->assigned > 0))
			{
				TORRENT_ASSERT(i->assigned <= i->request_size);
				tm.push_back(*i);
				i = m_queue.erase(i);
			}
			else
			{
				++i;
			}
			m_queued_bytes -= a;
		}

		while (!tm.empty())
		{
			bw_request<PeerConnection>& bwr = tm.back();
			bwr.peer->assign_bandwidth(m_channel, bwr.assigned);
			tm.pop_back();
		}
	}

	// these are the consumers that want bandwidth
	typedef std::vector<bw_request<PeerConnection> > queue_t;
	queue_t m_queue;
	// the number of bytes all the requests in queue are for
	int m_queued_bytes;

	// this is the channel within the consumers
	// that bandwidth is assigned to (upload or download)
	int m_channel;

	bool m_abort;

#ifdef TORRENT_VERBOSE_BANDWIDTH_LIMIT
	std::ofstream m_log;
	ptime m_start;
#endif
};

}

#endif

<commit_msg>fixed bug in ratelimiter's outstanding bytes counter<commit_after>/*

Copyright (c) 2007, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#ifndef TORRENT_BANDWIDTH_MANAGER_HPP_INCLUDED
#define TORRENT_BANDWIDTH_MANAGER_HPP_INCLUDED

#include <boost/shared_ptr.hpp>
#include <boost/intrusive_ptr.hpp>
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <boost/integer_traits.hpp>
#include <deque>

#ifdef TORRENT_VERBOSE_BANDWIDTH_LIMIT
#include <fstream>
#endif

#include "libtorrent/socket.hpp"
#include "libtorrent/invariant_check.hpp"
#include "libtorrent/assert.hpp"
#include "libtorrent/bandwidth_limit.hpp"
#include "libtorrent/bandwidth_queue_entry.hpp"

using boost::weak_ptr;
using boost::shared_ptr;
using boost::intrusive_ptr;
using boost::bind;

namespace libtorrent {

template<class PeerConnection>
struct bandwidth_manager
{
	bandwidth_manager(int channel
#ifdef TORRENT_VERBOSE_BANDWIDTH_LIMIT
		, bool log = false
#endif
		)
		: m_queued_bytes(0)
		, m_channel(channel)
		, m_abort(false)
	{
#ifdef TORRENT_VERBOSE_BANDWIDTH_LIMIT
		if (log)
			m_log.open("bandwidth_limiter.log", std::ios::trunc);
		m_start = time_now();
#endif
	}

	void close()
	{
		m_abort = true;
		m_queue.clear();
		m_queued_bytes = 0;
		error_code ec;
	}

#ifdef TORRENT_DEBUG
	bool is_queued(PeerConnection const* peer) const
	{
		for (typename queue_t::const_iterator i = m_queue.begin()
			, end(m_queue.end()); i != end; ++i)
		{
			if (i->peer.get() == peer) return true;
		}
		return false;
	}
#endif

	int queue_size() const
	{
		return m_queue.size();
	}

	int queued_bytes() const
	{
		return m_queued_bytes;
	}

	// non prioritized means that, if there's a line for bandwidth,
	// others will cut in front of the non-prioritized peers.
	// this is used by web seeds
	void request_bandwidth(intrusive_ptr<PeerConnection> const& peer
		, int blk, int priority
		, bandwidth_channel* chan1 = 0
		, bandwidth_channel* chan2 = 0
		, bandwidth_channel* chan3 = 0
		, bandwidth_channel* chan4 = 0
		, bandwidth_channel* chan5 = 0
		)
	{
		INVARIANT_CHECK;
		if (m_abort) return;

		TORRENT_ASSERT(blk > 0);
		TORRENT_ASSERT(priority > 0);
		TORRENT_ASSERT(!is_queued(peer.get()));

		bw_request<PeerConnection> bwr(peer, blk, priority);
		int i = 0;
		if (chan1 && chan1->throttle() > 0) bwr.channel[i++] = chan1;
		if (chan2 && chan2->throttle() > 0) bwr.channel[i++] = chan2;
		if (chan3 && chan3->throttle() > 0) bwr.channel[i++] = chan3;
		if (chan4 && chan4->throttle() > 0) bwr.channel[i++] = chan4;
		if (chan5 && chan5->throttle() > 0) bwr.channel[i++] = chan5;
		if (i == 0)
		{
			// the connection is not rate limited by any of its
			// bandwidth channels, or it doesn't belong to any
			// channels. There's no point in adding it to
			// the queue, just satisfy the request immediately
			bwr.peer->assign_bandwidth(m_channel, blk);
			return;
		}
		m_queued_bytes += blk;
		m_queue.push_back(bwr);
	}

#ifdef TORRENT_DEBUG
	void check_invariant() const
	{
		int queued = 0;
		for (typename queue_t::const_iterator i = m_queue.begin()
			, end(m_queue.end()); i != end; ++i)
		{
			queued += i->request_size - i->assigned;
		}
		TORRENT_ASSERT(queued == m_queued_bytes);
	}
#endif

	void update_quotas(time_duration const& dt)
	{
		if (m_abort) return;
		if (m_queue.empty()) return;

		INVARIANT_CHECK;

		int dt_milliseconds = total_milliseconds(dt);
		if (dt_milliseconds > 3000) dt_milliseconds = 3000;

		// for each bandwidth channel, call update_quota(dt)

		std::vector<bandwidth_channel*> channels;

		for (typename queue_t::iterator i = m_queue.begin();
			i != m_queue.end();)
		{
			if (i->peer->is_disconnecting())
			{
				m_queued_bytes -= i->request_size - i->assigned;

				// return all assigned quota to all the
				// bandwidth channels this peer belongs to
				for (int j = 0; j < 5 && i->channel[j]; ++j)
				{
					bandwidth_channel* bwc = i->channel[j];
					bwc->return_quota(i->assigned);
				}

				i = m_queue.erase(i);
				continue;
			}
			for (int j = 0; j < 5 && i->channel[j]; ++j)
			{
				bandwidth_channel* bwc = i->channel[j];
				bwc->tmp = 0;
			}
			++i;
		}

		for (typename queue_t::iterator i = m_queue.begin()
			, end(m_queue.end()); i != end; ++i)
		{
			for (int j = 0; j < 5 && i->channel[j]; ++j)
			{
				bandwidth_channel* bwc = i->channel[j];
				if (bwc->tmp == 0) channels.push_back(bwc);
				bwc->tmp += i->priority;
				TORRENT_ASSERT(i->priority > 0);
			}
		}

		for (std::vector<bandwidth_channel*>::iterator i = channels.begin()
			, end(channels.end()); i != end; ++i)
		{
			(*i)->update_quota(dt_milliseconds);
		}

		queue_t tm;

		for (typename queue_t::iterator i = m_queue.begin();
			i != m_queue.end();)
		{
			int a = i->assign_bandwidth();
			if (i->assigned == i->request_size
				|| (i->ttl <= 0 && i->assigned > 0))
			{
				a += i->request_size - i->assigned;
				TORRENT_ASSERT(i->assigned <= i->request_size);
				tm.push_back(*i);
				i = m_queue.erase(i);
			}
			else
			{
				++i;
			}
			m_queued_bytes -= a;
		}

		while (!tm.empty())
		{
			bw_request<PeerConnection>& bwr = tm.back();
			bwr.peer->assign_bandwidth(m_channel, bwr.assigned);
			tm.pop_back();
		}
	}

	// these are the consumers that want bandwidth
	typedef std::vector<bw_request<PeerConnection> > queue_t;
	queue_t m_queue;
	// the number of bytes all the requests in queue are for
	int m_queued_bytes;

	// this is the channel within the consumers
	// that bandwidth is assigned to (upload or download)
	int m_channel;

	bool m_abort;

#ifdef TORRENT_VERBOSE_BANDWIDTH_LIMIT
	std::ofstream m_log;
	ptime m_start;
#endif
};

}

#endif

<|endoftext|>


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

/*!
  \file window_view.hpp
  \brief Implements an isolated view on a window in a network

  \author Heinz Riener
*/

#pragma once

#include "../traits.hpp"
#include "../networks/detail/foreach.hpp"
#include "../utils/window_utils.hpp"
#include "immutable_view.hpp"
#include <algorithm>
#include <cassert>
#include <cstdint>
#include <set>
#include <unordered_map>
#include <vector>

namespace mockturtle
{

/*! \brief Implements an isolated view on a window in a network.
 *
 * This view creates a network from a window in a large network.  The
 * window is specified by three parameters:
 *   1.) `inputs` are the common support of all window nodes, they do
 *       not overlap with `gates` (i.e., the intersection of `inputs` and
 *       `gates` is the empty set).
 *   2.) `gates` are the nodes in the window, supported by the
 *       `inputs` (i.e., `gates` are in the transitive fanout of the
 *       `inputs`,
 *   3.) `outputs` are signals (regular or complemented nodes)
 *        pointing to nodes in `gates` or `inputs`.  Not all fanouts
 *        of an output node are already part of the window.
 *
 * The second parameter `gates` has to be passed and is not
 * automatically computed (for example in contrast to `cut_view`),
 * because there are different strategies to construct a window from a
 * support set.  The outputs could be automatically computed.
 *
 * The window_view implements one new API method:
 *   1.) `belongs_to`: takes a node (or a signal) and returns true if and
 *       only if the corresponding node belongs to the window
 */
template<typename Ntk>
class window_view : public immutable_view<Ntk>
{
public:
  using storage = typename Ntk::storage;
  using node = typename Ntk::node;
  using signal = typename Ntk::signal;

public:
  template<typename _Ntk = Ntk, typename = std::enable_if_t<!std::is_same_v<typename _Ntk::signal, typename _Ntk::node>>>
  explicit window_view( Ntk const& ntk, std::vector<node> const& inputs, std::vector<signal> const& outputs, std::vector<node> const& gates )
    : immutable_view<Ntk>( ntk )
    , _inputs( inputs )
    , _outputs( outputs )
  {
    construct( inputs, gates );
  }

  explicit window_view( Ntk const& ntk, std::vector<node> const& inputs, std::vector<node> const& outputs, std::vector<node> const& gates )
    : immutable_view<Ntk>( ntk )
    , _inputs( inputs )
  {
    construct( inputs, gates );

    /* convert output nodes to signals */
    std::transform( std::begin( outputs ), std::end( outputs ), std::back_inserter( _outputs ),
                    [this]( node const& n ){
                      return this->make_signal( n );
                    });
  }

#pragma region Window
  template<typename _Ntk = Ntk, typename = std::enable_if_t<!std::is_same_v<typename _Ntk::signal, typename _Ntk::node>>>
  inline bool belongs_to( signal const& s ) const
  {
    return std::find( std::begin( _nodes ), std::end( _nodes ), get_node( s ) ) != std::end( _nodes );
  }

  inline bool belongs_to( node const& n ) const
  {
    return std::find( std::begin( _nodes ), std::end( _nodes ), n ) != std::end( _nodes );
  }
#pragma endregion

#pragma region Structural properties
  inline uint32_t size() const
  {
    return static_cast<uint32_t>( _nodes.size() );
  }

  inline uint32_t num_cis() const
  {
    return num_pis();
  }

  inline uint32_t num_cos() const
  {
    return num_pos();
  }

  inline uint32_t num_latches() const
  {
    return 0u;
  }

  inline uint32_t num_pis() const
  {
    return static_cast<uint32_t>( _inputs.size() );
  }

  inline uint32_t num_pos() const
  {
    return static_cast<uint32_t>( _outputs.size() );
  }

  inline uint32_t num_registers() const
  {
    return 0u;
  }

  inline uint32_t num_gates() const
  {
    return static_cast<uint32_t>( _nodes.size() - _inputs.size() - 1u );
  }

  inline uint32_t fanout_size( node const& n ) const = delete;

  inline uint32_t node_to_index( node const& n ) const
  {
    return _node_to_index.at( n );
  }

  inline node index_to_node( uint32_t index ) const
  {
    return _nodes[index];
  }

  inline bool is_pi( node const& n ) const
  {
    return std::find( std::begin( _inputs ), std::end( _inputs ), n ) != std::end( _inputs );
  }

  inline bool is_ci( node const& n ) const
  {
    return is_pi( n );
  }
#pragma endregion

#pragma region Node and signal iterators
  template<typename Fn>
  void foreach_pi( Fn&& fn ) const
  {
    detail::foreach_element( std::begin( _inputs ), std::end( _inputs ), fn );
  }

  template<typename Fn>
  void foreach_po( Fn&& fn ) const
  {
    detail::foreach_element( std::begin( _outputs ), std::end( _outputs ), fn );
  }

  template<typename Fn>
  void foreach_ci( Fn&& fn ) const
  {
    foreach_pi( fn );
  }

  template<typename Fn>
  void foreach_co( Fn&& fn ) const
  {
    foreach_po( fn );
  }

  template<typename Fn>
  void foreach_ro( Fn&& fn ) const
  {
    (void)fn;
  }

  template<typename Fn>
  void foreach_ri( Fn&& fn ) const
  {
    (void)fn;
  }

  template<typename Fn>
  void foreach_register( Fn&& fn ) const
  {
    (void)fn;
  }

  template<typename Fn>
  void foreach_node( Fn&& fn ) const
  {
    detail::foreach_element( std::begin( _nodes ), std::end( _nodes ), fn );
  }

  template<typename Fn>
  void foreach_gate( Fn&& fn ) const
  {
    detail::foreach_element( std::begin( _nodes ) + 1u + _inputs.size(), std::end( _nodes ), fn );
  }

  template<typename Fn>
  void foreach_fanin( node const& n, Fn&& fn ) const
  {
    /* constants and inputs do not have fanins */
    if ( this->is_constant( n ) ||
         std::find( std::begin( _inputs ), std::end( _inputs ), n ) != std::end( _inputs ) )
    {
      return;
    }

    /* if it's not a window input, the node has to be a window node */
    assert( std::find( std::begin( _nodes ) + 1 + _inputs.size(), std::end( _nodes ), n ) != std::end( _nodes ) );
    immutable_view<Ntk>::foreach_fanin( n, fn );
  }
#pragma endregion

protected:
  void construct( std::vector<node> const& inputs, std::vector<node> const& gates )
  {
    /* copy constant to nodes */
    _nodes.emplace_back( this->get_node( this->get_constant( false ) ) );

    /* copy inputs to nodes */
    std::copy( std::begin( inputs ), std::end( inputs ), std::back_inserter( _nodes ) );

    /* copy gates to nodes */
    std::copy( std::begin( gates ), std::end( gates ), std::back_inserter( _nodes ) );

    /* create a mapping from node id (index in the original network) to window index */
    for ( uint32_t index = 0; index < _nodes.size(); ++index )
    {
      _node_to_index[_nodes.at( index )] = index;
    }
  }

protected:
  std::vector<node> _inputs;
  std::vector<signal> _outputs;
  std::vector<node> _nodes;
  std::unordered_map<node, uint32_t> _node_to_index;
}; /* window_view */

} /* namespace mockturtle */
<commit_msg>New API methods `foreach_internal_fanout` and `foreach_external_fanout` in `window_view`. (#399)<commit_after>/* mockturtle: C++ logic network library
 * Copyright (C) 2018-2020  EPFL
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

/*!
  \file window_view.hpp
  \brief Implements an isolated view on a window in a network

  \author Heinz Riener
*/

#pragma once

#include "../traits.hpp"
#include "../networks/detail/foreach.hpp"
#include "../utils/window_utils.hpp"
#include "immutable_view.hpp"

#include <algorithm>
#include <cassert>
#include <cstdint>
#include <unordered_map>
#include <vector>

namespace mockturtle
{

/*! \brief Implements an isolated view on a window in a network.
 *
 * This view creates a network from a window in a large network.  The
 * window is specified by three parameters:
 *   1.) `inputs` are the common support of all window nodes, they do
 *       not overlap with `gates` (i.e., the intersection of `inputs` and
 *       `gates` is the empty set).
 *   2.) `gates` are the nodes in the window, supported by the
 *       `inputs` (i.e., `gates` are in the transitive fanout of the
 *       `inputs`,
 *   3.) `outputs` are signals (regular or complemented nodes)
 *        pointing to nodes in `gates` or `inputs`.  Not all fanouts
 *        of an output node are already part of the window.
 *
 * The second parameter `gates` has to be passed and is not
 * automatically computed (for example in contrast to `cut_view`),
 * because there are different strategies to construct a window from a
 * support set.  The outputs could be automatically computed.
 *
 * The window_view implements three new API methods:
 *   1.) `belongs_to`: takes a node (or a signal) and returns true if and
 *       only if the corresponding node belongs to the window
 *   2.) `foreach_internal_fanout`: takes a node and invokes a predicate
         on all fanout nodes of the node that belong to the window
 *   3.) `foreach_exnteral_fanout`: takes a node and invokes a predicate
         on all fanouts of the node that do not belong to the window
 */
template<typename Ntk>
class window_view : public immutable_view<Ntk>
{
public:
  using storage = typename Ntk::storage;
  using node = typename Ntk::node;
  using signal = typename Ntk::signal;

public:
  template<typename _Ntk = Ntk, typename = std::enable_if_t<!std::is_same_v<typename _Ntk::signal, typename _Ntk::node>>>
  explicit window_view( Ntk const& ntk, std::vector<node> const& inputs, std::vector<signal> const& outputs, std::vector<node> const& gates )
    : immutable_view<Ntk>( ntk )
    , _inputs( inputs )
    , _outputs( outputs )
  {
    construct( inputs, gates );
  }

  explicit window_view( Ntk const& ntk, std::vector<node> const& inputs, std::vector<node> const& outputs, std::vector<node> const& gates )
    : immutable_view<Ntk>( ntk )
    , _inputs( inputs )
  {
    construct( inputs, gates );

    /* convert output nodes to signals */
    std::transform( std::begin( outputs ), std::end( outputs ), std::back_inserter( _outputs ),
                    [this]( node const& n ){
                      return this->make_signal( n );
                    });
  }

#pragma region Window
  template<typename _Ntk = Ntk, typename = std::enable_if_t<!std::is_same_v<typename _Ntk::signal, typename _Ntk::node>>>
  inline bool belongs_to( signal const& s ) const
  {
    return std::find( std::begin( _nodes ), std::end( _nodes ), get_node( s ) ) != std::end( _nodes );
  }

  inline bool belongs_to( node const& n ) const
  {
    return std::find( std::begin( _nodes ), std::end( _nodes ), n ) != std::end( _nodes );
  }
#pragma endregion

#pragma region Structural properties
  inline uint32_t size() const
  {
    return static_cast<uint32_t>( _nodes.size() );
  }

  inline uint32_t num_cis() const
  {
    return num_pis();
  }

  inline uint32_t num_cos() const
  {
    return num_pos();
  }

  inline uint32_t num_latches() const
  {
    return 0u;
  }

  inline uint32_t num_pis() const
  {
    return static_cast<uint32_t>( _inputs.size() );
  }

  inline uint32_t num_pos() const
  {
    return static_cast<uint32_t>( _outputs.size() );
  }

  inline uint32_t num_registers() const
  {
    return 0u;
  }

  inline uint32_t num_gates() const
  {
    return static_cast<uint32_t>( _nodes.size() - _inputs.size() - 1u );
  }

  inline uint32_t fanout_size( node const& n ) const = delete;

  inline uint32_t node_to_index( node const& n ) const
  {
    return _node_to_index.at( n );
  }

  inline node index_to_node( uint32_t index ) const
  {
    return _nodes[index];
  }

  inline bool is_pi( node const& n ) const
  {
    return std::find( std::begin( _inputs ), std::end( _inputs ), n ) != std::end( _inputs );
  }

  inline bool is_ci( node const& n ) const
  {
    return is_pi( n );
  }

  signal po_at( uint32_t index ) const
  {
    assert( index < _outputs.size() );
    return *( std::begin( _outputs ) + index );
  }

  signal co_at( uint32_t index ) const
  {
    return po_at( index );
  }
#pragma endregion

#pragma region Node and signal iterators
  template<typename Fn>
  void foreach_pi( Fn&& fn ) const
  {
    detail::foreach_element( std::begin( _inputs ), std::end( _inputs ), fn );
  }

  template<typename Fn>
  void foreach_po( Fn&& fn ) const
  {
    detail::foreach_element( std::begin( _outputs ), std::end( _outputs ), fn );
  }

  template<typename Fn>
  void foreach_ci( Fn&& fn ) const
  {
    foreach_pi( fn );
  }

  template<typename Fn>
  void foreach_co( Fn&& fn ) const
  {
    foreach_po( fn );
  }

  template<typename Fn>
  void foreach_ro( Fn&& fn ) const
  {
    (void)fn;
  }

  template<typename Fn>
  void foreach_ri( Fn&& fn ) const
  {
    (void)fn;
  }

  template<typename Fn>
  void foreach_register( Fn&& fn ) const
  {
    (void)fn;
  }

  template<typename Fn>
  void foreach_node( Fn&& fn ) const
  {
    detail::foreach_element( std::begin( _nodes ), std::end( _nodes ), fn );
  }

  template<typename Fn>
  void foreach_gate( Fn&& fn ) const
  {
    detail::foreach_element( std::begin( _nodes ) + 1u + _inputs.size(), std::end( _nodes ), fn );
  }

  template<typename Fn>
  void foreach_fanin( node const& n, Fn&& fn ) const
  {
    /* constants and inputs do not have fanins */
    if ( this->is_constant( n ) ||
         std::find( std::begin( _inputs ), std::end( _inputs ), n ) != std::end( _inputs ) )
    {
      return;
    }

    /* if it's not a window input, the node has to be a window node */
    assert( std::find( std::begin( _nodes ) + 1 + _inputs.size(), std::end( _nodes ), n ) != std::end( _nodes ) );
    immutable_view<Ntk>::foreach_fanin( n, fn );
  }

  template<typename Fn>
  void foreach_internal_fanout( node const& n, Fn&& fn ) const
  {
    this->foreach_fanout( n, [&]( node const& fo ){
      if ( tbelongs_to( fo ) )
      {
        fn( fo );
      }
    });
  }

  template<typename Fn>
  void foreach_external_fanout( node const& n, Fn&& fn ) const
  {
    this->foreach_fanout( n, [&]( node const& fo ){
      if ( !belongs_to( fo ) )
      {
        fn( fo );
      }
    });
  }
#pragma endregion

protected:
  void construct( std::vector<node> const& inputs, std::vector<node> const& gates )
  {
    /* copy constant to nodes */
    _nodes.emplace_back( this->get_node( this->get_constant( false ) ) );

    /* copy inputs to nodes */
    std::copy( std::begin( inputs ), std::end( inputs ), std::back_inserter( _nodes ) );

    /* copy gates to nodes */
    std::copy( std::begin( gates ), std::end( gates ), std::back_inserter( _nodes ) );

    /* create a mapping from node id (index in the original network) to window index */
    for ( uint32_t index = 0; index < _nodes.size(); ++index )
    {
      _node_to_index[_nodes.at( index )] = index;
    }
  }

protected:
  std::vector<node> _inputs;
  std::vector<signal> _outputs;
  std::vector<node> _nodes;
  std::unordered_map<node, uint32_t> _node_to_index;
}; /* window_view */

} /* namespace mockturtle */
<|endoftext|>


<commit_before>#ifndef RBX_UTIL_THREAD_HPP
#define RBX_UTIL_THREAD_HPP

#define USE_PTHREADS

#ifdef USE_PTHREADS
#include <pthread.h>
#include <errno.h>
#include <signal.h>
#include <assert.h>

#include <sstream>
#include <iostream>

namespace thread {
  enum Code {
    cLocked,
    cUnlocked,
    cLockBusy,
    cNotYours,
    cReady,
    cTimedOut
  };

#if 0
  static void block_all_signals() {
    sigset_t set;
    sigfillset(&set);

    pthread_sigmask(SIG_BLOCK, &set, NULL);
  }

  static void accept_signal(int sig) {
    sigset_t set;
    sigemptyset(&set);
    sigaddset(&set, sig);

    pthread_sigmask(SIG_UNBLOCK, &set, NULL);
  }

  static void ignore_signal(int sig) {
    sigset_t set;
    sigemptyset(&set);
    sigaddset(&set, sig);

    pthread_sigmask(SIG_BLOCK, &set, NULL);
  }

  static void wait_on_signal(int sig) {
    sigset_t set;
    sigemptyset(&set);
    sigaddset(&set, sig);

    int bunk;
    sigwait(&set, &bunk);
  }
#endif
  template <typename T = void*>
  class ThreadData {
    pthread_key_t native_;

  public:
    ThreadData() {
      assert(pthread_key_create(&native_, NULL) == 0);
    }

    ~ThreadData() {
      assert(pthread_key_delete(native_) == 0);
    }

    T get() {
      return reinterpret_cast<T>(pthread_getspecific(native_));
    }

    void set(T val) {
      assert(pthread_setspecific(native_, reinterpret_cast<void*>(val)) == 0);
    }
  };

  class Thread {
    pthread_t native_;
    bool delete_on_exit_;
    size_t stack_size_;

    static void* trampoline(void* arg) {
      Thread* self = reinterpret_cast<Thread*>(arg);
      self->perform();
      if(self->delete_on_exit()) delete self;
      return NULL;
    }

  public:
    Thread(size_t stack_size = 0, pthread_t tid = 0)
      : native_(tid)
      , stack_size_(stack_size)
    {
      delete_on_exit_ = (tid != 0);
    }

    virtual ~Thread() { }

    pthread_t* native() {
      return &native_;
    }

    void run() {
      pthread_attr_t attrs;
      pthread_attr_init(&attrs);
      if(stack_size_) {
        pthread_attr_setstacksize(&attrs, stack_size_);
      }

      assert(pthread_create(&native_, &attrs, trampoline, (void*)this) == 0);
    }

    virtual void perform() { }

    void detach() {
      assert(pthread_detach(native_) == 0);
    }

    bool equal(Thread& other) {
      if(pthread_equal(native_, *other.native())) {
        return true;
      }

      return false;
    }

    void join() {
      void* bunk;
      int err = pthread_join(native_, &bunk);
      if(err != 0) {
        if(err == EDEADLK) {
          std::cout << "Thread deadlock!\n";
        }

        assert(0);
      }
    }

    void cancel() {
      assert(pthread_cancel(native_) == 0);
    }

    void kill(int sig) {
      assert(pthread_kill(native_, sig) == 0);
    }

    int priority() {
      int _policy;
      struct sched_param params;

      assert(pthread_getschedparam(native_, &_policy, &params) == 0);

      return params.sched_priority;
    }

    bool set_priority(int priority) {
      int _policy;
      struct sched_param params;

      assert(pthread_getschedparam(native_, &_policy, &params) == 0);
      int max = sched_get_priority_max(_policy);
      int min = sched_get_priority_min(_policy);

      if(min > priority) {
        priority = min;
      }
      else if(max < priority) {
        priority = max;
      }

      params.sched_priority = priority;

      int err = pthread_setschedparam(native_, _policy, &params);
      if(err == ENOTSUP) return false;

      return true;
    }

    bool delete_on_exit() {
      return delete_on_exit_;
    }

    void set_delete_on_exit() {
      delete_on_exit_ = true;
    }
  };

  /*
   * A stacklet object for locking and unlocking.
   */

#ifdef DEBUG_LOCKGUARD
  const bool cDebugLockGuard = true;
#else
  const bool cDebugLockGuard = false;
#endif

  template <class T>
  class LockGuardTemplate {
  public:
    T& lock_;
    bool locked_;

    LockGuardTemplate(T& in_lock, bool initial = false)
      : lock_(in_lock)
      , locked_(initial)
    { }

    void lock() {
      if(locked_) return;
      if(cDebugLockGuard) {
        std::cout << "[[ " << pthread_self() << "   Locking " << lock_.describe() << " ]]\n";
      }
      lock_.lock();
      locked_ = true;
      if(cDebugLockGuard) {
        std::cout << "[[ " << pthread_self() << "   Locked " << lock_.describe() << " ]]\n";
      }
    }

    void unlock() {
      if(!locked_) return;
      if(cDebugLockGuard) {
        std::cout << "[[ " << pthread_self() << " Unlocking " << lock_.describe() << " ]]\n";
      }
      lock_.unlock();
      locked_ = false;
    }
  };

  template <class T>
  class StackLockGuard : public LockGuardTemplate<T> {
  public:
    StackLockGuard(T& in_lock)
      : LockGuardTemplate<T>(in_lock, false)
    {
      this->lock();
    }

    ~StackLockGuard() {
      this->unlock();
    }
  };

  template <class T>
  class StackUnlockGuard : public LockGuardTemplate<T> {
  public:
    StackUnlockGuard(T& lock_obj)
      : LockGuardTemplate<T>(lock_obj, true)
    {
      this->unlock();
    }

    ~StackUnlockGuard() {
      this->lock();
    }
  };

  class Mutex {
  public: // Types
    typedef StackLockGuard<Mutex> LockGuard;
    typedef StackUnlockGuard<Mutex> UnlockGuard;

  private:
    pthread_mutex_t native_;

  public:
    Mutex() {
      assert(pthread_mutex_init(&native_, NULL) == 0);
    }

    ~Mutex() {
      int err = pthread_mutex_destroy(&native_);
      if(err != 0) {
        if(err == EBUSY) assert(0 && "mutex is busy!");
        if(err == EINVAL) assert(0 && "mutex is dead!");
        assert(0 && "mutex is screwed!");
      }
    }

    pthread_mutex_t* native() {
      return &native_;
    }

    void lock() {
      int err;
      if(cDebugLockGuard) {
        std::cout << "[[ " << pthread_self() << "   MLocking " << describe() << " ]]\n";
      }
      if((err = pthread_mutex_lock(&native_)) != 0) {
        if(err == EDEADLK) {
          std::cout << "Thread deadlock!\n";
        }

        assert(0);
      }
      if(cDebugLockGuard) {
        std::cout << "[[ " << pthread_self() << "    MLocked " << describe() << " ]]\n";
      }
    }

    Code try_lock() {
      int err = pthread_mutex_trylock(&native_);
      if(err != 0) {
        if(err == EBUSY) return cLockBusy;
        assert(0);
      }

      return cLocked;
    }

    Code unlock() {
      if(cDebugLockGuard) {
        std::cout << "[[ " << pthread_self() << "   MUnlocking " << describe() << " ]]\n";
      }
      int err = pthread_mutex_unlock(&native_);
      if(err != 0) {
        if(err == EPERM) return cNotYours;
        assert(0);
      }

      return cUnlocked;
    }

    std::string describe() {
      std::stringstream ss;
      ss << "Mutex ";
      ss << (void*)this;
      return ss.str();
    }
  };

  class Condition {
    pthread_cond_t native_;
    bool ready_;

  public:
    Condition()
      : ready_(false)
    {
      assert(pthread_cond_init(&native_, NULL) == 0);
    }

    ~Condition() {
      assert(pthread_cond_destroy(&native_) == 0);
    }

    void reset() {
      ready_ = false;
    }

    void signal() {
      ready_ = true;
      assert(pthread_cond_signal(&native_) == 0);
    }

    void broadcast() {
      ready_ = true;
      assert(pthread_cond_broadcast(&native_) == 0);
    }

    void wait(Mutex& mutex) {
      if(cDebugLockGuard) {
        std::cout << "[[ " << pthread_self() << "   CUnlocking " << mutex.describe() << " ]]\n";
      }
      while(!ready_) {
        assert(pthread_cond_wait(&native_, mutex.native()) == 0);
      }

      if(cDebugLockGuard) {
        std::cout << "[[ " << pthread_self() << "   CLocked " << mutex.describe() << " ]]\n";
      }
    }

    Code wait_until(Mutex& mutex, const struct timespec* ts) {
      if(cDebugLockGuard) {
        std::cout << "[[ " << pthread_self() << "   CUnlocking " << mutex.describe() << " ]]\n";
      }

      while(!ready_) {
        int err = pthread_cond_timedwait(&native_, mutex.native(), ts);
        if(cDebugLockGuard) {
          std::cout << "[[ " << pthread_self() << "   CLocked " << mutex.describe() << " ]]\n";
        }

        if(err != 0) {
          if(err == ETIMEDOUT) {
            if(ready_) return cReady;
            return cTimedOut;
          }

          switch(err) {
          case EINVAL:
            // This is not really correct, but it works for us:
            // We treat this error as ONLY ts being invalid, ie, it's for
            // a time in the past. Thus we can just say everything is ready.
            //
            // EINVAL can mean that both native_ and mutex.native() are invalid
            // too, but we've got no recourse if that is true.
            return cReady;
          default:
            std::cout << "Unknown failure from pthread_cond_timedwait!\n";
          }

          assert(0);
        }
      }

      return cReady;
    }
  };
}

#ifdef HAVE_OSX_SPINLOCK

#include <libkern/OSAtomic.h>

namespace thread {

  class SpinLock {
  public: // Types
    typedef StackLockGuard<SpinLock> LockGuard;
    typedef StackUnlockGuard<SpinLock> UnlockGuard;

  private:
    OSSpinLock native_;

  public:
    SpinLock()
      : native_(0)
    {}

    void lock() {
      OSSpinLockLock(&native_);
    }

    void unlock() {
      OSSpinLockUnlock(&native_);
    }

    Code try_lock() {
      if(OSSpinLockTry(&native_)) {
        return cLockBusy;
      }

      return cLocked;
    }

    std::string describe() {
      std::stringstream ss;
      ss << "SpinLock ";
      ss << (void*)this;
      return ss.str();
    }
  };
};

#else

namespace thread {
  typedef Mutex SpinLock;
};

#endif

#else

#error "No thread implementation defined"

#endif

#endif
<commit_msg>Make thread::Mutex recursive by default<commit_after>#ifndef RBX_UTIL_THREAD_HPP
#define RBX_UTIL_THREAD_HPP

#define USE_PTHREADS

#ifdef USE_PTHREADS
#include <pthread.h>
#include <errno.h>
#include <signal.h>
#include <assert.h>

#include <sstream>
#include <iostream>

namespace thread {
  enum Code {
    cLocked,
    cUnlocked,
    cLockBusy,
    cNotYours,
    cReady,
    cTimedOut
  };

#if 0
  static void block_all_signals() {
    sigset_t set;
    sigfillset(&set);

    pthread_sigmask(SIG_BLOCK, &set, NULL);
  }

  static void accept_signal(int sig) {
    sigset_t set;
    sigemptyset(&set);
    sigaddset(&set, sig);

    pthread_sigmask(SIG_UNBLOCK, &set, NULL);
  }

  static void ignore_signal(int sig) {
    sigset_t set;
    sigemptyset(&set);
    sigaddset(&set, sig);

    pthread_sigmask(SIG_BLOCK, &set, NULL);
  }

  static void wait_on_signal(int sig) {
    sigset_t set;
    sigemptyset(&set);
    sigaddset(&set, sig);

    int bunk;
    sigwait(&set, &bunk);
  }
#endif
  template <typename T = void*>
  class ThreadData {
    pthread_key_t native_;

  public:
    ThreadData() {
      assert(pthread_key_create(&native_, NULL) == 0);
    }

    ~ThreadData() {
      assert(pthread_key_delete(native_) == 0);
    }

    T get() {
      return reinterpret_cast<T>(pthread_getspecific(native_));
    }

    void set(T val) {
      assert(pthread_setspecific(native_, reinterpret_cast<void*>(val)) == 0);
    }
  };

  class Thread {
    pthread_t native_;
    bool delete_on_exit_;
    size_t stack_size_;

    static void* trampoline(void* arg) {
      Thread* self = reinterpret_cast<Thread*>(arg);
      self->perform();
      if(self->delete_on_exit()) delete self;
      return NULL;
    }

  public:
    Thread(size_t stack_size = 0, pthread_t tid = 0)
      : native_(tid)
      , stack_size_(stack_size)
    {
      delete_on_exit_ = (tid != 0);
    }

    virtual ~Thread() { }

    pthread_t* native() {
      return &native_;
    }

    void run() {
      pthread_attr_t attrs;
      pthread_attr_init(&attrs);
      if(stack_size_) {
        pthread_attr_setstacksize(&attrs, stack_size_);
      }

      assert(pthread_create(&native_, &attrs, trampoline, (void*)this) == 0);
    }

    virtual void perform() { }

    void detach() {
      assert(pthread_detach(native_) == 0);
    }

    bool equal(Thread& other) {
      if(pthread_equal(native_, *other.native())) {
        return true;
      }

      return false;
    }

    void join() {
      void* bunk;
      int err = pthread_join(native_, &bunk);
      if(err != 0) {
        if(err == EDEADLK) {
          std::cout << "Thread deadlock!\n";
        }

        assert(0);
      }
    }

    void cancel() {
      assert(pthread_cancel(native_) == 0);
    }

    void kill(int sig) {
      assert(pthread_kill(native_, sig) == 0);
    }

    int priority() {
      int _policy;
      struct sched_param params;

      assert(pthread_getschedparam(native_, &_policy, &params) == 0);

      return params.sched_priority;
    }

    bool set_priority(int priority) {
      int _policy;
      struct sched_param params;

      assert(pthread_getschedparam(native_, &_policy, &params) == 0);
      int max = sched_get_priority_max(_policy);
      int min = sched_get_priority_min(_policy);

      if(min > priority) {
        priority = min;
      }
      else if(max < priority) {
        priority = max;
      }

      params.sched_priority = priority;

      int err = pthread_setschedparam(native_, _policy, &params);
      if(err == ENOTSUP) return false;

      return true;
    }

    bool delete_on_exit() {
      return delete_on_exit_;
    }

    void set_delete_on_exit() {
      delete_on_exit_ = true;
    }
  };

  /*
   * A stacklet object for locking and unlocking.
   */

#ifdef DEBUG_LOCKGUARD
  const bool cDebugLockGuard = true;
#else
  const bool cDebugLockGuard = false;
#endif

  template <class T>
  class LockGuardTemplate {
  public:
    T& lock_;
    bool locked_;

    LockGuardTemplate(T& in_lock, bool initial = false)
      : lock_(in_lock)
      , locked_(initial)
    { }

    void lock() {
      if(locked_) return;
      if(cDebugLockGuard) {
        std::cout << "[[ " << pthread_self() << "   Locking " << lock_.describe() << " ]]\n";
      }
      lock_.lock();
      locked_ = true;
      if(cDebugLockGuard) {
        std::cout << "[[ " << pthread_self() << "   Locked " << lock_.describe() << " ]]\n";
      }
    }

    void unlock() {
      if(!locked_) return;
      if(cDebugLockGuard) {
        std::cout << "[[ " << pthread_self() << " Unlocking " << lock_.describe() << " ]]\n";
      }
      lock_.unlock();
      locked_ = false;
    }
  };

  template <class T>
  class StackLockGuard : public LockGuardTemplate<T> {
  public:
    StackLockGuard(T& in_lock)
      : LockGuardTemplate<T>(in_lock, false)
    {
      this->lock();
    }

    ~StackLockGuard() {
      this->unlock();
    }
  };

  template <class T>
  class StackUnlockGuard : public LockGuardTemplate<T> {
  public:
    StackUnlockGuard(T& lock_obj)
      : LockGuardTemplate<T>(lock_obj, true)
    {
      this->unlock();
    }

    ~StackUnlockGuard() {
      this->lock();
    }
  };

  class Mutex {
  public: // Types
    typedef StackLockGuard<Mutex> LockGuard;
    typedef StackUnlockGuard<Mutex> UnlockGuard;

  private:
    pthread_mutex_t native_;

  public:
    Mutex() {
      pthread_mutexattr_t attr;
      pthread_mutexattr_init(&attr);
      pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
      assert(pthread_mutex_init(&native_, &attr) == 0);
    }

    ~Mutex() {
      int err = pthread_mutex_destroy(&native_);
      if(err != 0) {
        if(err == EBUSY) assert(0 && "mutex is busy!");
        if(err == EINVAL) assert(0 && "mutex is dead!");
        assert(0 && "mutex is screwed!");
      }
    }

    pthread_mutex_t* native() {
      return &native_;
    }

    void lock() {
      int err;
      if(cDebugLockGuard) {
        std::cout << "[[ " << pthread_self() << "   MLocking " << describe() << " ]]\n";
      }
      if((err = pthread_mutex_lock(&native_)) != 0) {
        if(err == EDEADLK) {
          std::cout << "Thread deadlock!\n";
        }

        assert(0);
      }
      if(cDebugLockGuard) {
        std::cout << "[[ " << pthread_self() << "    MLocked " << describe() << " ]]\n";
      }
    }

    Code try_lock() {
      int err = pthread_mutex_trylock(&native_);
      if(err != 0) {
        if(err == EBUSY) return cLockBusy;
        assert(0);
      }

      return cLocked;
    }

    Code unlock() {
      if(cDebugLockGuard) {
        std::cout << "[[ " << pthread_self() << "   MUnlocking " << describe() << " ]]\n";
      }
      int err = pthread_mutex_unlock(&native_);
      if(err != 0) {
        if(err == EPERM) return cNotYours;
        assert(0);
      }

      return cUnlocked;
    }

    std::string describe() {
      std::stringstream ss;
      ss << "Mutex ";
      ss << (void*)this;
      return ss.str();
    }
  };

  class Condition {
    pthread_cond_t native_;
    bool ready_;

  public:
    Condition()
      : ready_(false)
    {
      assert(pthread_cond_init(&native_, NULL) == 0);
    }

    ~Condition() {
      assert(pthread_cond_destroy(&native_) == 0);
    }

    void reset() {
      ready_ = false;
    }

    void signal() {
      ready_ = true;
      assert(pthread_cond_signal(&native_) == 0);
    }

    void broadcast() {
      ready_ = true;
      assert(pthread_cond_broadcast(&native_) == 0);
    }

    void wait(Mutex& mutex) {
      if(cDebugLockGuard) {
        std::cout << "[[ " << pthread_self() << "   CUnlocking " << mutex.describe() << " ]]\n";
      }
      while(!ready_) {
        assert(pthread_cond_wait(&native_, mutex.native()) == 0);
      }

      if(cDebugLockGuard) {
        std::cout << "[[ " << pthread_self() << "   CLocked " << mutex.describe() << " ]]\n";
      }
    }

    Code wait_until(Mutex& mutex, const struct timespec* ts) {
      if(cDebugLockGuard) {
        std::cout << "[[ " << pthread_self() << "   CUnlocking " << mutex.describe() << " ]]\n";
      }

      while(!ready_) {
        int err = pthread_cond_timedwait(&native_, mutex.native(), ts);
        if(cDebugLockGuard) {
          std::cout << "[[ " << pthread_self() << "   CLocked " << mutex.describe() << " ]]\n";
        }

        if(err != 0) {
          if(err == ETIMEDOUT) {
            if(ready_) return cReady;
            return cTimedOut;
          }

          switch(err) {
          case EINVAL:
            // This is not really correct, but it works for us:
            // We treat this error as ONLY ts being invalid, ie, it's for
            // a time in the past. Thus we can just say everything is ready.
            //
            // EINVAL can mean that both native_ and mutex.native() are invalid
            // too, but we've got no recourse if that is true.
            return cReady;
          default:
            std::cout << "Unknown failure from pthread_cond_timedwait!\n";
          }

          assert(0);
        }
      }

      return cReady;
    }
  };
}

#ifdef HAVE_OSX_SPINLOCK

#include <libkern/OSAtomic.h>

namespace thread {

  class SpinLock {
  public: // Types
    typedef StackLockGuard<SpinLock> LockGuard;
    typedef StackUnlockGuard<SpinLock> UnlockGuard;

  private:
    OSSpinLock native_;

  public:
    SpinLock()
      : native_(0)
    {}

    void lock() {
      OSSpinLockLock(&native_);
    }

    void unlock() {
      OSSpinLockUnlock(&native_);
    }

    Code try_lock() {
      if(OSSpinLockTry(&native_)) {
        return cLockBusy;
      }

      return cLocked;
    }

    std::string describe() {
      std::stringstream ss;
      ss << "SpinLock ";
      ss << (void*)this;
      return ss.str();
    }
  };
};

#else

namespace thread {
  typedef Mutex SpinLock;
};

#endif

#else

#error "No thread implementation defined"

#endif

#endif
<|endoftext|>