<commit_before>/*************************************************************************
 *
 *  $RCSfile: SlsScrollBarManager.hxx,v $
 *
 *  $Revision: 1.4 $
 *
 *  last change: $Author: obo $ $Date: 2005-04-12 16:58:40 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (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.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#ifndef SD_SLIDESORTER_SLIDE_SORTER_SCROLL_BAR_MANAGER_HXX
#define SD_SLIDESORTER_SLIDE_SORTER_SCROLL_BAR_MANAGER_HXX

#include <tools/link.hxx>
#include <tools/gen.hxx>
#include <vcl/timer.hxx>
#include <memory>

class Point;
class Rectangle;
class ScrollBar;
class ScrollBarBox;
class Window;

namespace sd { namespace slidesorter { namespace controller {

class SlideSorterController;

/** Manage the horizontal and vertical scroll bars.  Listen for events, set
    their sizes, place them in the window, determine their visibilities.

Handle auto scrolling, i.e. the scrolling of the window when the
    mouse comes near the window border while dragging a selection.

In order to make the slide sorter be used in the task pane with its
    own vertical scrollbars the vertical scrollbar of the use of the slide
    sorter is optional.  When using it the available area in a window is
    used and the vertical scrollbar is displayed when that area is not large
    enough.  When the vertical scrollbar is not used then the available area
    is assumed to be modifiable.  In that case the PlaceScrollBars() method
    may return an area larger than the one given.

*/
class ScrollBarManager
{
public:
    /** Create a new scroll bar manager that manages three controls: the two
        given scroll bars and a little window it creates and ownes that
        fills the gap at the bottom right corner that is left between the
        two scroll bars.  Call LateInitialization() after constructing a new
        object.
        @param pParentWindow
            This window is used to create the little filler window.
        @param pContentWindow
            This window holds the content.  Its view area is controlled by
            the scroll bars.  The content window is used to transform
            coordinates and, if the vertical scroll bar is not in use, is
            resized to fully show its content.
        @param pHorizontalScrollBar
            The horizontal scroll bar that is controlled by the new
            manager.  Its ownership remains with the caller.
        @param pVerticalScrollBar
            The horizontal scroll bar that is controlled by the new
            manager.  Its ownership remains with the caller.
        @param pScrollBarFiller
            The task of this filler is to paint the little square enclosed
            by the two scroll bars when they are both visible.
    */
    ScrollBarManager (
        SlideSorterController& rController,
        ::Window* pParentWindow,
        ::Window* pContentWindow,
        ScrollBar* pHorizontalScrollBar,
        ScrollBar* pVerticalScrollBar,
        ScrollBarBox* pScrollBarFiller);

    ~ScrollBarManager (void);

    /** Call this method after constructing a new object of this class.
    */
    void LateInitialization (void);

    /** Set up the scroll bar, i.e. thumb size and position.  Call this
        method when the content of the browser window changed, i.e. pages
        were inserted or deleted, the layout or the zoom factor has
        changed.
        @param bResetThumbPosition
            When <TRUE/> then set the thumb position to position 0.  This is
            done when e.g. switching between master page mode and draw mode.
        @param bScrollToCurrentPosition
            When <TRUE/> then scroll the window to the new offset that is
            defined by the scroll bars.  Otherwise the new offset is simply
            set and the whole window is repainted.
    */
    void UpdateScrollBars (
        bool bResetThumbPosition = false,
        bool bScrollToCurrentPosition = true);

    /** Place the scroll bars inside the given area.  When the available
        area is not large enough for the content to display the resulting
        behaviour depends on the mbUseVerticalScrollBar flag.  When it is
        set to true then a vertical scroll bar is shown.  Otherwise the
        height of the returned area is enlarged so that the content fits
        into it.
        @param rAvailableArea
            The scroll bars will be placed inside this rectangle.  It is
            expected to be given in pixel relative to its parent.
        @return
            Returns the space that remains after the scroll bars are
            placed.  When the mbUseVerticalScrollBar flag is false then the
            returned rectangle may be larger than the given one.
    */
    Rectangle PlaceScrollBars (const Rectangle& rAvailableArea);

    /** Update the vertical scroll bar so that the visible area has the
        given top value.
    */
    void SetTop (long nTop);

    /** Return the width of the vertical scroll bar, which--when
        shown--should be fixed in contrast to its height.
        @return
           Returns 0 when the vertical scroll bar is not shown or does not
           exist, otherwise its width in pixel is returned.
    */
    int GetVerticalScrollBarWidth (void) const;

    /** Return the height of the horizontal scroll bar, which--when
        shown--should be fixed in contrast to its width.
        @return
           Returns 0 when the vertical scroll bar is not shown or does not
           exist, otherwise its height in pixel is returned.
    */
    int GetHorizontalScrollBarHeight (void) const;

    /** Call this method to scroll a window while the mouse is in dragging a
        selection.  If the mouse is near the window border or is outside the
        window then scroll the window accordingly.
        @return
            When the window is scrolled then this method returns <TRUE/>.
            When the window is not changed then <FALSE/> is returned.
    */
    bool AutoScroll (const Point& rMouseWindowPosition);

    void StopAutoScroll (void);

private:
    SlideSorterController& mrController;
    /** The horizontal scroll bar.  Note that is used but not owned by
        objects of this class.  It is given to the constructor.
    */
    ScrollBar* mpHorizontalScrollBar;
    /** The vertical scroll bar.  Note that is used but not owned by
        objects of this class.  It is given to the constructor.
    */
    ScrollBar* mpVerticalScrollBar;
    /// Relative horizontal position of the visible area in the view.
    double mnHorizontalPosition;
    /// Relative vertical position of the visible area in the view.
    double mnVerticalPosition;
    /** The width and height of the border at the inside of the window which
        when entered while in drag mode leads to a scrolling of the window.
    */
    Size maScrollBorder;
    double mnHorizontalScrollFactor;
    double mnVerticalScrollFactor;
    /** The only task of this little window is to paint the little square at
        the bottom right corner left by the two scroll bars (when both are
        visible).
    */
    ScrollBarBox* mpScrollBarFiller;

    /** The auto scroll timer is used for keep scrolling the window when the
        mouse reaches its border while dragging a selection.  When the mouse
        is not moved the timer issues events to keep scrolling.
    */
    Timer maAutoScrollTimer;
    Size maAutoScrollOffset;

    /** The content window is the one whose view port is controlled by the
        scroll bars.
    */
    ::Window* mpContentWindow;

    void SetWindowOrigin (
        double nHorizontalPosition,
        double nVerticalPosition);

    /** Determine the visibility of the scroll bars so that the window
        content is not clipped in any dimension without showing a scroll
        bar.
        @param rAvailableArea
            The area in which the scroll bars, the scroll bar filler, and
            the SlideSorterView will be placed.
        @return
            The area that is enclosed by the scroll bars is returned.  It
            will be filled with the SlideSorterView.
    */
    Rectangle DetermineScrollBarVisibilities (const Rectangle& rAvailableArea);

    /** Typically called by DetermineScrollBarVisibilities() this method
        tests a specific configuration of the two scroll bars being visible
        or hidden.
        @return
            When the window content can be shown with only being clipped in
            an orientation where the scroll bar would be shown then <TRUE/>
            is returned.
    */
    bool TestScrollBarVisibilities (
        bool bHorizontalScrollBarVisible,
        bool bVerticalScrollBarVisible,
        const Rectangle& rAvailableArea);

    void CalcAutoScrollOffset (const Point& rMouseWindowPosition);
    bool RepeatAutoScroll (void);

    DECL_LINK(HorizontalScrollBarHandler, ScrollBar*);
    DECL_LINK(VerticalScrollBarHandler, ScrollBar*);
    DECL_LINK(AutoScrollTimeoutHandler, Timer*);

    void PlaceHorizontalScrollBar (const Rectangle& aArea);
    void PlaceVerticalScrollBar (const Rectangle& aArea);
    void PlaceFiller (const Rectangle& aArea);

    /** Make the height of the content window larger or smaller, so that the

        content size fits exactly in.  This is achieved by changing the size
        of the parent window and rely on the resulting resize.
    */
    void AdaptWindowSize (const Rectangle& rArea);
};

} } } // end of namespace ::sd::slidesorter::controller

#endif
<commit_msg>INTEGRATION: CWS ooo19126 (1.4.134); FILE MERGED 2005/09/05 13:24:29 rt 1.4.134.1: #i54170# Change license header: remove SISSL<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: SlsScrollBarManager.hxx,v $
 *
 *  $Revision: 1.5 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-09 06:19:30 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef SD_SLIDESORTER_SLIDE_SORTER_SCROLL_BAR_MANAGER_HXX
#define SD_SLIDESORTER_SLIDE_SORTER_SCROLL_BAR_MANAGER_HXX

#include <tools/link.hxx>
#include <tools/gen.hxx>
#include <vcl/timer.hxx>
#include <memory>

class Point;
class Rectangle;
class ScrollBar;
class ScrollBarBox;
class Window;

namespace sd { namespace slidesorter { namespace controller {

class SlideSorterController;

/** Manage the horizontal and vertical scroll bars.  Listen for events, set
    their sizes, place them in the window, determine their visibilities.

Handle auto scrolling, i.e. the scrolling of the window when the
    mouse comes near the window border while dragging a selection.

In order to make the slide sorter be used in the task pane with its
    own vertical scrollbars the vertical scrollbar of the use of the slide
    sorter is optional.  When using it the available area in a window is
    used and the vertical scrollbar is displayed when that area is not large
    enough.  When the vertical scrollbar is not used then the available area
    is assumed to be modifiable.  In that case the PlaceScrollBars() method
    may return an area larger than the one given.

*/
class ScrollBarManager
{
public:
    /** Create a new scroll bar manager that manages three controls: the two
        given scroll bars and a little window it creates and ownes that
        fills the gap at the bottom right corner that is left between the
        two scroll bars.  Call LateInitialization() after constructing a new
        object.
        @param pParentWindow
            This window is used to create the little filler window.
        @param pContentWindow
            This window holds the content.  Its view area is controlled by
            the scroll bars.  The content window is used to transform
            coordinates and, if the vertical scroll bar is not in use, is
            resized to fully show its content.
        @param pHorizontalScrollBar
            The horizontal scroll bar that is controlled by the new
            manager.  Its ownership remains with the caller.
        @param pVerticalScrollBar
            The horizontal scroll bar that is controlled by the new
            manager.  Its ownership remains with the caller.
        @param pScrollBarFiller
            The task of this filler is to paint the little square enclosed
            by the two scroll bars when they are both visible.
    */
    ScrollBarManager (
        SlideSorterController& rController,
        ::Window* pParentWindow,
        ::Window* pContentWindow,
        ScrollBar* pHorizontalScrollBar,
        ScrollBar* pVerticalScrollBar,
        ScrollBarBox* pScrollBarFiller);

    ~ScrollBarManager (void);

    /** Call this method after constructing a new object of this class.
    */
    void LateInitialization (void);

    /** Set up the scroll bar, i.e. thumb size and position.  Call this
        method when the content of the browser window changed, i.e. pages
        were inserted or deleted, the layout or the zoom factor has
        changed.
        @param bResetThumbPosition
            When <TRUE/> then set the thumb position to position 0.  This is
            done when e.g. switching between master page mode and draw mode.
        @param bScrollToCurrentPosition
            When <TRUE/> then scroll the window to the new offset that is
            defined by the scroll bars.  Otherwise the new offset is simply
            set and the whole window is repainted.
    */
    void UpdateScrollBars (
        bool bResetThumbPosition = false,
        bool bScrollToCurrentPosition = true);

    /** Place the scroll bars inside the given area.  When the available
        area is not large enough for the content to display the resulting
        behaviour depends on the mbUseVerticalScrollBar flag.  When it is
        set to true then a vertical scroll bar is shown.  Otherwise the
        height of the returned area is enlarged so that the content fits
        into it.
        @param rAvailableArea
            The scroll bars will be placed inside this rectangle.  It is
            expected to be given in pixel relative to its parent.
        @return
            Returns the space that remains after the scroll bars are
            placed.  When the mbUseVerticalScrollBar flag is false then the
            returned rectangle may be larger than the given one.
    */
    Rectangle PlaceScrollBars (const Rectangle& rAvailableArea);

    /** Update the vertical scroll bar so that the visible area has the
        given top value.
    */
    void SetTop (long nTop);

    /** Return the width of the vertical scroll bar, which--when
        shown--should be fixed in contrast to its height.
        @return
           Returns 0 when the vertical scroll bar is not shown or does not
           exist, otherwise its width in pixel is returned.
    */
    int GetVerticalScrollBarWidth (void) const;

    /** Return the height of the horizontal scroll bar, which--when
        shown--should be fixed in contrast to its width.
        @return
           Returns 0 when the vertical scroll bar is not shown or does not
           exist, otherwise its height in pixel is returned.
    */
    int GetHorizontalScrollBarHeight (void) const;

    /** Call this method to scroll a window while the mouse is in dragging a
        selection.  If the mouse is near the window border or is outside the
        window then scroll the window accordingly.
        @return
            When the window is scrolled then this method returns <TRUE/>.
            When the window is not changed then <FALSE/> is returned.
    */
    bool AutoScroll (const Point& rMouseWindowPosition);

    void StopAutoScroll (void);

private:
    SlideSorterController& mrController;
    /** The horizontal scroll bar.  Note that is used but not owned by
        objects of this class.  It is given to the constructor.
    */
    ScrollBar* mpHorizontalScrollBar;
    /** The vertical scroll bar.  Note that is used but not owned by
        objects of this class.  It is given to the constructor.
    */
    ScrollBar* mpVerticalScrollBar;
    /// Relative horizontal position of the visible area in the view.
    double mnHorizontalPosition;
    /// Relative vertical position of the visible area in the view.
    double mnVerticalPosition;
    /** The width and height of the border at the inside of the window which
        when entered while in drag mode leads to a scrolling of the window.
    */
    Size maScrollBorder;
    double mnHorizontalScrollFactor;
    double mnVerticalScrollFactor;
    /** The only task of this little window is to paint the little square at
        the bottom right corner left by the two scroll bars (when both are
        visible).
    */
    ScrollBarBox* mpScrollBarFiller;

    /** The auto scroll timer is used for keep scrolling the window when the
        mouse reaches its border while dragging a selection.  When the mouse
        is not moved the timer issues events to keep scrolling.
    */
    Timer maAutoScrollTimer;
    Size maAutoScrollOffset;

    /** The content window is the one whose view port is controlled by the
        scroll bars.
    */
    ::Window* mpContentWindow;

    void SetWindowOrigin (
        double nHorizontalPosition,
        double nVerticalPosition);

    /** Determine the visibility of the scroll bars so that the window
        content is not clipped in any dimension without showing a scroll
        bar.
        @param rAvailableArea
            The area in which the scroll bars, the scroll bar filler, and
            the SlideSorterView will be placed.
        @return
            The area that is enclosed by the scroll bars is returned.  It
            will be filled with the SlideSorterView.
    */
    Rectangle DetermineScrollBarVisibilities (const Rectangle& rAvailableArea);

    /** Typically called by DetermineScrollBarVisibilities() this method
        tests a specific configuration of the two scroll bars being visible
        or hidden.
        @return
            When the window content can be shown with only being clipped in
            an orientation where the scroll bar would be shown then <TRUE/>
            is returned.
    */
    bool TestScrollBarVisibilities (
        bool bHorizontalScrollBarVisible,
        bool bVerticalScrollBarVisible,
        const Rectangle& rAvailableArea);

    void CalcAutoScrollOffset (const Point& rMouseWindowPosition);
    bool RepeatAutoScroll (void);

    DECL_LINK(HorizontalScrollBarHandler, ScrollBar*);
    DECL_LINK(VerticalScrollBarHandler, ScrollBar*);
    DECL_LINK(AutoScrollTimeoutHandler, Timer*);

    void PlaceHorizontalScrollBar (const Rectangle& aArea);
    void PlaceVerticalScrollBar (const Rectangle& aArea);
    void PlaceFiller (const Rectangle& aArea);

    /** Make the height of the content window larger or smaller, so that the

        content size fits exactly in.  This is achieved by changing the size
        of the parent window and rely on the resulting resize.
    */
    void AdaptWindowSize (const Rectangle& rArea);
};

} } } // end of namespace ::sd::slidesorter::controller

#endif
<|endoftext|>


<commit_before>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2022.  All rights reserved
 */
#include "../StroikaPreComp.h"

#include "../Containers/Collection.h"
#include "../Time/Realtime.h"

#include "Synchronized.h"
#include "Thread.h"

#include "IntervalTimer.h"

using namespace Stroika::Foundation;
using namespace Stroika::Foundation::Containers;
using namespace Stroika::Foundation::Execution;
using namespace Stroika::Foundation::Time;

/*
 ********************************************************************************
 ******************** IntervalTimer::Manager::DefaultRep ************************
 ********************************************************************************
 */
struct IntervalTimer::Manager::DefaultRep ::Rep_ {
    Rep_ ()
    {
    }
    void AddOneShot (const TimerCallback& intervalTimer, const Time::Duration& when)
    {
        auto lk = fData_.rwget ();
        lk->Add (Elt_{intervalTimer, Time::GetTickCount () + when.As<DurationSecondsType> ()});
        DataChanged_ ();
    }
    void AddRepeating (const TimerCallback& intervalTimer, const Time::Duration& repeatInterval, const optional<Time::Duration>& hysteresis)
    {
        auto lk = fData_.rwget ();
        lk->Add (Elt_{intervalTimer, Time::GetTickCount () + repeatInterval.As<DurationSecondsType> (), repeatInterval, hysteresis});
        DataChanged_ ();
    }
    void RemoveRepeating (const TimerCallback& intervalTimer) noexcept
    {
        auto lk = fData_.rwget ();
        Verify (lk->RemoveIf ([&] (const Elt_& i) { return i.fCallback == intervalTimer; }));
        DataChanged_ ();
    }

    struct Elt_ {
        Function<void (void)> fCallback;
        DurationSecondsType   fCallNextAt;
        optional<Duration>    fFrequency;
        optional<Duration>    fHisteresis;
    };
    // @todo - re-implement using priority q, with next time at top of q
    Synchronized<Collection<Elt_>>   fData_;
    Synchronized<Thread::CleanupPtr> sThread_{Execution::Thread::CleanupPtr::eAbortBeforeWaiting};
    WaitableEvent                    fDataChanged_{WaitableEvent::ResetType::eAutoReset};

    // this is where a priorityq would be better
    DurationSecondsType GetNextWakeupTime_ ()
    {
        auto dataLock = fData_.cget ();
        Assert (not dataLock->empty ());
        DurationSecondsType r = kInfinite;
        for (Elt_ i : dataLock.cref ()) {
            r = min (r, i.fCallNextAt);
        }
        return r;
    }
    void RunnerLoop_ ()
    {
        // keep checking for timer events to run
        while (true) {
            DurationSecondsType wakeupAt = GetNextWakeupTime_ ();
            fDataChanged_.WaitUntilQuietly (wakeupAt);
            // now process any timer events that are ready (could easily be more than one).
            // if we had a priority q, we would do them in order, but for now, just do all that are ready
            // NOTE - to avoid holding a lock (in case these guys remove themselves or whatever) - lock/run through list twice
            DurationSecondsType now      = Time::GetTickCount ();
            Collection<Elt_>    elts2Run = fData_.cget ()->Where ([=] (const Elt_& i) { return i.fCallNextAt <= now; });
            for (Elt_ i : elts2Run) {
                IgnoreExceptionsExceptThreadAbortForCall (i.fCallback ());
            }
            // now reset the 'next' time for each run element
            auto rwDataLock = fData_.rwget ();
            now             = Time::GetTickCount ();
            for (Elt_ i : elts2Run) {
                if (i.fFrequency.has_value ()) {
                    Elt_ newE = i;
                    // @todo add hysteresis
                    newE.fCallNextAt = now + newE.fFrequency->As<DurationSecondsType> ();
                    auto updateI     = rwDataLock->Find ([&] (const Elt_& ii) { return ii.fCallback == i.fCallback; });
                    rwDataLock->Update (updateI, newE);
                }
                else {
                    Verify (rwDataLock->RemoveIf ([&] (const Elt_& ii) { return ii.fCallback == i.fCallback; }));
                }
            }
        }
    }
    void DataChanged_ ()
    {
        auto rwThreadLk = sThread_.rwget ();
        if (fData_.cget ()->empty ()) {
            rwThreadLk->operator= (nullptr);
        }
        else {
            auto lk = sThread_.rwget ();
            if (lk.cref () == nullptr) {
                lk->operator= (Thread::New ([this] () {
                    RunnerLoop_ ();
                },
                                            Thread::eAutoStart, L"Default-Interval-Timer"sv));
            }
        }
    }
};

IntervalTimer::Manager::DefaultRep::DefaultRep ()
    : fHiddenRep_{make_shared<Rep_> ()}
{
}

void IntervalTimer::Manager::DefaultRep::AddOneShot (const TimerCallback& intervalTimer, const Time::Duration& when)
{
    fHiddenRep_->AddOneShot (intervalTimer, when);
}

void IntervalTimer::Manager::DefaultRep::AddRepeating (const TimerCallback& intervalTimer, const Time::Duration& repeatInterval, const optional<Time::Duration>& hysteresis)
{
    fHiddenRep_->AddRepeating (intervalTimer, repeatInterval, hysteresis);
}

void IntervalTimer::Manager::DefaultRep::RemoveRepeating (const TimerCallback& intervalTimer) noexcept
{
    fHiddenRep_->RemoveRepeating (intervalTimer);
}

/*
 ********************************************************************************
 ********************* IntervalTimer::Manager::Activator ************************
 ********************************************************************************
 */
IntervalTimer::Manager::Activator::Activator ()
{
    Require (Manager::sThe.fRep_ == nullptr); // only one activator object allowed
    Manager::sThe = move (Manager{make_shared<IntervalTimer::Manager::DefaultRep> ()});
}

IntervalTimer::Manager::Activator::~Activator ()
{
    Require (Manager::sThe.fRep_ != nullptr); // this is the only way to remove, and so must not be null here
    Manager::sThe.fRep_.reset ();
}
<commit_msg>minor clenaups to IntervalTimer::Manager<commit_after>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2022.  All rights reserved
 */
#include "../StroikaPreComp.h"

#include "../Containers/Collection.h"
#include "../Time/Realtime.h"

#include "Synchronized.h"
#include "Thread.h"

#include "IntervalTimer.h"

using namespace Stroika::Foundation;
using namespace Stroika::Foundation::Containers;
using namespace Stroika::Foundation::Execution;
using namespace Stroika::Foundation::Time;

/*
 ********************************************************************************
 ******************** IntervalTimer::Manager::DefaultRep ************************
 ********************************************************************************
 */
struct IntervalTimer::Manager::DefaultRep ::Rep_ {
    Rep_ ()
    {
    }
    void AddOneShot (const TimerCallback& intervalTimer, const Time::Duration& when)
    {
        auto lk = fData_.rwget ();
        lk->Add (Elt_{intervalTimer, Time::GetTickCount () + when.As<DurationSecondsType> ()});
        DataChanged_ ();
    }
    void AddRepeating (const TimerCallback& intervalTimer, const Time::Duration& repeatInterval, const optional<Time::Duration>& hysteresis)
    {
        auto lk = fData_.rwget ();
        lk->Add (Elt_{intervalTimer, Time::GetTickCount () + repeatInterval.As<DurationSecondsType> (), repeatInterval, hysteresis});
        DataChanged_ ();
    }
    void RemoveRepeating (const TimerCallback& intervalTimer) noexcept
    {
        auto lk = fData_.rwget ();
        Verify (lk->RemoveIf ([&] (const Elt_& i) { return i.fCallback == intervalTimer; }));
        DataChanged_ ();
    }

    struct Elt_ {
        Function<void (void)> fCallback;
        DurationSecondsType   fCallNextAt;
        optional<Duration>    fFrequency;
        optional<Duration>    fHisteresis;
    };
    // @todo - re-implement using priority q, with next time at top of q
    Synchronized<Collection<Elt_>>   fData_;
    Synchronized<Thread::CleanupPtr> sThread_{Execution::Thread::CleanupPtr::eAbortBeforeWaiting};
    WaitableEvent                    fDataChanged_{WaitableEvent::ResetType::eAutoReset};

    // this is where a priorityq would be better
    DurationSecondsType GetNextWakeupTime_ ()
    {
        auto dataLock = fData_.cget ();
        Assert (not dataLock->empty ());
        DurationSecondsType r = kInfinite;
        for (Elt_ i : dataLock.cref ()) {
            r = min (r, i.fCallNextAt);
        }
        return r;
    }
    void RunnerLoop_ ()
    {
        // keep checking for timer events to run
        while (true) {
            DurationSecondsType wakeupAt = GetNextWakeupTime_ ();
            fDataChanged_.WaitUntilQuietly (wakeupAt);
            // now process any timer events that are ready (could easily be more than one).
            // if we had a priority q, we would do them in order, but for now, just do all that are ready
            // NOTE - to avoid holding a lock (in case these guys remove themselves or whatever) - lock/run through list twice
            DurationSecondsType now      = Time::GetTickCount ();
            Collection<Elt_>    elts2Run = fData_.cget ()->Where ([=] (const Elt_& i) { return i.fCallNextAt <= now; });
            for (Elt_ i : elts2Run) {
                IgnoreExceptionsExceptThreadAbortForCall (i.fCallback ());
            }
            // now reset the 'next' time for each run element
            auto rwDataLock = fData_.rwget ();
            now             = Time::GetTickCount ();
            for (Elt_ i : elts2Run) {
                if (i.fFrequency.has_value ()) {
                    Elt_ newE = i;
                    // @todo add hysteresis
                    newE.fCallNextAt = now + newE.fFrequency->As<DurationSecondsType> ();
                    auto updateI     = rwDataLock->Find ([&] (const Elt_& ii) { return ii.fCallback == i.fCallback; });
                    rwDataLock->Update (updateI, newE);
                }
                else {
                    Verify (rwDataLock->RemoveIf ([&] (const Elt_& ii) { return ii.fCallback == i.fCallback; }));
                }
            }
        }
    }
    void DataChanged_ ()
    {
        auto rwThreadLk = sThread_.rwget ();
        if (fData_.cget ()->empty ()) {
            rwThreadLk->operator= (nullptr);
        }
        else {
            auto lk = sThread_.rwget ();
            if (lk.cref () == nullptr) {
                lk->operator= (Thread::New ([this] () {
                    RunnerLoop_ ();
                },
                                            Thread::eAutoStart, L"Default-Interval-Timer"sv));
            }
        }
    }
};

IntervalTimer::Manager::DefaultRep::DefaultRep ()
    : fHiddenRep_{make_shared<Rep_> ()}
{
}

void IntervalTimer::Manager::DefaultRep::AddOneShot (const TimerCallback& intervalTimer, const Time::Duration& when)
{
    AssertNotNull (fHiddenRep_);
    fHiddenRep_->AddOneShot (intervalTimer, when);
}

void IntervalTimer::Manager::DefaultRep::AddRepeating (const TimerCallback& intervalTimer, const Time::Duration& repeatInterval, const optional<Time::Duration>& hysteresis)
{
    AssertNotNull (fHiddenRep_);
    fHiddenRep_->AddRepeating (intervalTimer, repeatInterval, hysteresis);
}

void IntervalTimer::Manager::DefaultRep::RemoveRepeating (const TimerCallback& intervalTimer) noexcept
{
    AssertNotNull (fHiddenRep_);
    fHiddenRep_->RemoveRepeating (intervalTimer);
}

/*
 ********************************************************************************
 ********************* IntervalTimer::Manager::Activator ************************
 ********************************************************************************
 */
IntervalTimer::Manager::Activator::Activator ()
{
    Require (Manager::sThe.fRep_ == nullptr); // only one activator object allowed
    Manager::sThe = Manager{make_shared<IntervalTimer::Manager::DefaultRep> ()};
}

IntervalTimer::Manager::Activator::~Activator ()
{
    RequireNotNull (Manager::sThe.fRep_); // this is the only way to remove, and so must not be null here
    Manager::sThe.fRep_.reset ();
}
<|endoftext|>


<commit_before>/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.

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

#include "tensorflow/compiler/tf2xla/shape_util.h"
#include "tensorflow/compiler/tf2xla/xla_helpers.h"
#include "tensorflow/compiler/tf2xla/xla_op_kernel.h"
#include "tensorflow/compiler/tf2xla/xla_op_registry.h"
#include "tensorflow/core/framework/tensor_shape.h"

namespace tensorflow {
namespace {

class ReverseSequenceOp : public XlaOpKernel {
 public:
  explicit ReverseSequenceOp(OpKernelConstruction* context)
      : XlaOpKernel(context) {
    OP_REQUIRES_OK(context, context->GetAttr("batch_dim", &batch_dim_));
    OP_REQUIRES_OK(context, context->GetAttr("seq_dim", &seq_dim_));
  }

  void Compile(XlaOpKernelContext* context) override {
    const TensorShape input_shape = context->InputShape(0);
    const TensorShape seq_lens_shape = context->InputShape(1);

    OP_REQUIRES(context, TensorShapeUtils::IsVector(seq_lens_shape),
                errors::InvalidArgument("seq_lens input must be 1-dim, not ",
                                        seq_lens_shape.dims()));
    OP_REQUIRES(context, batch_dim_ != seq_dim_,
                errors::InvalidArgument("batch_dim == seq_dim == ", seq_dim_));
    OP_REQUIRES(
        context, seq_dim_ < input_shape.dims(),
        errors::InvalidArgument("seq_dim must be < input.dims()", "( ",
                                seq_dim_, " vs. ", input_shape.dims(), ")"));
    OP_REQUIRES(
        context, batch_dim_ < input_shape.dims(),
        errors::InvalidArgument("batch_dim must be < input.dims()", "( ",
                                batch_dim_, " vs. ", input_shape.dims(), ")"));
    OP_REQUIRES(
        context,
        seq_lens_shape.num_elements() == input_shape.dim_size(batch_dim_),
        errors::InvalidArgument("len(seq_lens) != input.dims(", batch_dim_,
                                "), ", "(", seq_lens_shape.num_elements(),
                                " vs. ", input_shape.dim_size(batch_dim_)));

    xla::XlaBuilder* builder = context->builder();
    const auto input = context->Input(0);
    const auto seq_lens = context->Input(1);

    const int64 batch_size = input_shape.dim_size(batch_dim_);

    const DataType input_type = context->input_type(0);
    const DataType seq_lens_type = context->input_type(1);
    const int64 max_seq_len = input_shape.dim_size(seq_dim_);

    xla::Shape input_xla_shape;
    OP_REQUIRES_OK(context, TensorShapeToXLAShape(input_type, input_shape,
                                                  &input_xla_shape));
    xla::Shape seq_lens_xla_shape;
    OP_REQUIRES_OK(context, TensorShapeToXLAShape(seq_lens_type, seq_lens_shape,
                                                  &seq_lens_xla_shape));

    const auto tuple_shape = xla::ShapeUtil::MakeTupleShape({
        xla::ShapeUtil::MakeShape(seq_lens_xla_shape.element_type(), {}),
        seq_lens_xla_shape,
        input_xla_shape,
    });

    // For each entry in the batch, reverse the sequence.
    // TODO(b/65689298): generalize the Map() operator to non-scalar cases and
    // use it here, instead of a While loop.

    // Condition: lambda (i, _, _): i < batch_size
    auto condition_builder =
        builder->CreateSubBuilder("reverse_sequence_condition");
    {
      auto param = condition_builder->Parameter(0, tuple_shape, "param");
      auto i = condition_builder->GetTupleElement(param, 0);
      condition_builder->Lt(
          i, XlaHelpers::IntegerLiteral(condition_builder.get(), seq_lens_type,
                                        batch_size));
    }
    auto condition = condition_builder->Build();
    OP_REQUIRES_OK(context, condition.status());

    auto body_builder = builder->CreateSubBuilder("reverse_sequence_body");
    {
      auto param = body_builder->Parameter(0, tuple_shape, "param");
      auto i = body_builder->GetTupleElement(param, 0);
      auto seq_lens = body_builder->GetTupleElement(param, 1);
      auto output = body_builder->GetTupleElement(param, 2);

      // seq_len is the sequence length of the current batch element (rank 1)
      auto seq_len = body_builder->DynamicSlice(
          seq_lens, body_builder->Reshape(i, {1}), {1});

      // Indices is the offset of the batch element in the input.
      auto indices = body_builder->Broadcast(
          XlaHelpers::Zero(body_builder.get(), seq_lens_type),
          {input_shape.dims()});
      indices = body_builder->DynamicUpdateSlice(
          indices, body_builder->Reshape(i, {1}),
          body_builder->Reshape(
              XlaHelpers::IntegerLiteral(body_builder.get(), seq_lens_type,
                                         batch_dim_),
              {1}));

      // slice_indices is the offset of the start of the reversed sequence in
      // the input.
      auto slice_indices = body_builder->DynamicUpdateSlice(
          indices,
          body_builder->Sub(XlaHelpers::IntegerLiteral(
                                body_builder.get(), seq_lens_type, max_seq_len),
                            seq_len),
          body_builder->Reshape(
              XlaHelpers::IntegerLiteral(body_builder.get(), seq_lens_type,
                                         seq_dim_),
              {1}));

      // Slice out the reversed sequence. The slice will overflow the end of the
      // sequence, and the contents of the overflow are implementation-defined.
      // However, we will mask off these elements and replace them with elements
      // from the original input so their values do not matter.
      TensorShape slice_shape = input_shape;
      slice_shape.set_dim(batch_dim_, 1);
      auto slice = body_builder->DynamicSlice(output, slice_indices,
                                              slice_shape.dim_sizes());

      // Shift the reversed sequence to the left.
      output = body_builder->DynamicUpdateSlice(output, slice, indices);

      body_builder->Tuple(
          {body_builder->Add(
               i, XlaHelpers::One(body_builder.get(), seq_lens_type)),
           seq_lens, output});
    }
    auto body = body_builder->Build();
    OP_REQUIRES_OK(context, body.status());

    auto loop_output = builder->While(
        condition.ValueOrDie(), body.ValueOrDie(),
        builder->Tuple({XlaHelpers::Zero(builder, seq_lens_type), seq_lens,
                        builder->Rev(input, {seq_dim_})}));
    auto output = builder->GetTupleElement(loop_output, 2);

    // Mask out elements after the sequence length.
    xla::XlaOp iota;
    OP_REQUIRES_OK(
        context, XlaHelpers::Iota(builder, seq_lens_type, max_seq_len, &iota));
    std::vector<int64> dims(input_shape.dims(), 1);
    dims[batch_dim_] = batch_size;
    auto mask = builder->Lt(iota, builder->Reshape(seq_lens, dims), {seq_dim_});

    // Broadcast the mask up to the input shape.
    mask =
        builder->Or(mask, builder->Broadcast(builder->ConstantR0<bool>(false),
                                             input_shape.dim_sizes()));

    output = builder->Select(mask, output, input);
    context->SetOutput(0, output);
  }

 private:
  int32 batch_dim_;
  int32 seq_dim_;
};

REGISTER_XLA_OP(Name("ReverseSequence"), ReverseSequenceOp);

}  // namespace
}  // namespace tensorflow
<commit_msg>[TF:XLA] Do not rely on implementation-defined semantics of DynamicSlice.<commit_after>/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.

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

#include "tensorflow/compiler/tf2xla/shape_util.h"
#include "tensorflow/compiler/tf2xla/xla_helpers.h"
#include "tensorflow/compiler/tf2xla/xla_op_kernel.h"
#include "tensorflow/compiler/tf2xla/xla_op_registry.h"
#include "tensorflow/core/framework/tensor_shape.h"

namespace tensorflow {
namespace {

class ReverseSequenceOp : public XlaOpKernel {
 public:
  explicit ReverseSequenceOp(OpKernelConstruction* context)
      : XlaOpKernel(context) {
    OP_REQUIRES_OK(context, context->GetAttr("batch_dim", &batch_dim_));
    OP_REQUIRES_OK(context, context->GetAttr("seq_dim", &seq_dim_));
  }

  void Compile(XlaOpKernelContext* context) override {
    const TensorShape input_shape = context->InputShape(0);
    const TensorShape seq_lens_shape = context->InputShape(1);

    OP_REQUIRES(context, TensorShapeUtils::IsVector(seq_lens_shape),
                errors::InvalidArgument("seq_lens input must be 1-dim, not ",
                                        seq_lens_shape.dims()));
    OP_REQUIRES(context, batch_dim_ != seq_dim_,
                errors::InvalidArgument("batch_dim == seq_dim == ", seq_dim_));
    OP_REQUIRES(
        context, seq_dim_ < input_shape.dims(),
        errors::InvalidArgument("seq_dim must be < input.dims()", "( ",
                                seq_dim_, " vs. ", input_shape.dims(), ")"));
    OP_REQUIRES(
        context, batch_dim_ < input_shape.dims(),
        errors::InvalidArgument("batch_dim must be < input.dims()", "( ",
                                batch_dim_, " vs. ", input_shape.dims(), ")"));
    OP_REQUIRES(
        context,
        seq_lens_shape.num_elements() == input_shape.dim_size(batch_dim_),
        errors::InvalidArgument("len(seq_lens) != input.dims(", batch_dim_,
                                "), ", "(", seq_lens_shape.num_elements(),
                                " vs. ", input_shape.dim_size(batch_dim_)));

    xla::XlaBuilder* builder = context->builder();
    const auto input = context->Input(0);
    const auto seq_lens = context->Input(1);

    const int64 batch_size = input_shape.dim_size(batch_dim_);

    const DataType input_type = context->input_type(0);
    const DataType seq_lens_type = context->input_type(1);
    const int64 max_seq_len = input_shape.dim_size(seq_dim_);

    xla::Shape input_xla_shape;
    OP_REQUIRES_OK(context, TensorShapeToXLAShape(input_type, input_shape,
                                                  &input_xla_shape));
    xla::Shape seq_lens_xla_shape;
    OP_REQUIRES_OK(context, TensorShapeToXLAShape(seq_lens_type, seq_lens_shape,
                                                  &seq_lens_xla_shape));

    const auto tuple_shape = xla::ShapeUtil::MakeTupleShape({
        xla::ShapeUtil::MakeShape(seq_lens_xla_shape.element_type(), {}),
        seq_lens_xla_shape,
        input_xla_shape,
    });

    // For each entry in the batch, reverse the sequence.
    // TODO(b/65689298): generalize the Map() operator to non-scalar cases and
    // use it here, instead of a While loop.

    // Condition: lambda (i, _, _): i < batch_size
    auto condition_builder =
        builder->CreateSubBuilder("reverse_sequence_condition");
    {
      auto param = condition_builder->Parameter(0, tuple_shape, "param");
      auto i = condition_builder->GetTupleElement(param, 0);
      condition_builder->Lt(
          i, XlaHelpers::IntegerLiteral(condition_builder.get(), seq_lens_type,
                                        batch_size));
    }
    auto condition = condition_builder->Build();
    OP_REQUIRES_OK(context, condition.status());

    auto body_builder = builder->CreateSubBuilder("reverse_sequence_body");
    {
      auto param = body_builder->Parameter(0, tuple_shape, "param");
      auto i = body_builder->GetTupleElement(param, 0);
      auto seq_lens = body_builder->GetTupleElement(param, 1);
      auto output = body_builder->GetTupleElement(param, 2);

      // seq_len is the sequence length of the current batch element (rank 1)
      auto seq_len = body_builder->DynamicSlice(
          seq_lens, body_builder->Reshape(i, {1}), {1});

      // Indices is the offset of the batch element in the input.
      auto batch_element_indices = body_builder->Broadcast(
          XlaHelpers::Zero(body_builder.get(), seq_lens_type),
          {input_shape.dims()});
      batch_element_indices = body_builder->DynamicUpdateSlice(
          batch_element_indices, body_builder->Reshape(i, {1}),
          body_builder->Reshape(
              XlaHelpers::IntegerLiteral(body_builder.get(), seq_lens_type,
                                         batch_dim_),
              {1}));

      // Slice out the current batch element and pad it out in the sequence
      // dimension.
      TensorShape slice_shape = input_shape;
      slice_shape.set_dim(batch_dim_, 1);
      slice_shape.set_dim(seq_dim_, max_seq_len);
      auto slice = body_builder->DynamicSlice(output, batch_element_indices,
                                              slice_shape.dim_sizes());
      auto padding_config = xla::MakeNoPaddingConfig(slice_shape.dims());
      padding_config.mutable_dimensions(seq_dim_)->set_edge_padding_high(
          slice_shape.dim_size(seq_dim_));
      slice = body_builder->Pad(
          slice, XlaHelpers::Zero(body_builder.get(), input_type),
          padding_config);

      // Now slice out the reversed sequence from its actual start.
      // sequence_start_indices is the offset of the start of the reversed
      // sequence in the input. The slice will go into the padding, however, we
      // will mask off these elements and replace them with elements from the
      // original input so their values do not matter.
      auto sequence_start_indices = body_builder->Broadcast(
          XlaHelpers::Zero(body_builder.get(), seq_lens_type),
          {slice_shape.dims()});
      sequence_start_indices = body_builder->DynamicUpdateSlice(
          sequence_start_indices,
          body_builder->Sub(XlaHelpers::IntegerLiteral(
                                body_builder.get(), seq_lens_type, max_seq_len),
                            seq_len),
          body_builder->Reshape(
              XlaHelpers::IntegerLiteral(body_builder.get(), seq_lens_type,
                                         seq_dim_),
              {1}));
      slice = body_builder->DynamicSlice(slice, sequence_start_indices,
                                         slice_shape.dim_sizes());

      // Shift the reversed sequence to the left.
      output = body_builder->DynamicUpdateSlice(output, slice,
                                                batch_element_indices);

      body_builder->Tuple(
          {body_builder->Add(
               i, XlaHelpers::One(body_builder.get(), seq_lens_type)),
           seq_lens, output});
    }
    auto body = body_builder->Build();
    OP_REQUIRES_OK(context, body.status());

    auto loop_output = builder->While(
        condition.ValueOrDie(), body.ValueOrDie(),
        builder->Tuple({XlaHelpers::Zero(builder, seq_lens_type), seq_lens,
                        builder->Rev(input, {seq_dim_})}));
    auto output = builder->GetTupleElement(loop_output, 2);

    // Mask out elements after the sequence length.
    xla::XlaOp iota;
    OP_REQUIRES_OK(
        context, XlaHelpers::Iota(builder, seq_lens_type, max_seq_len, &iota));
    std::vector<int64> dims(input_shape.dims(), 1);
    dims[batch_dim_] = batch_size;
    auto mask = builder->Lt(iota, builder->Reshape(seq_lens, dims), {seq_dim_});

    // Broadcast the mask up to the input shape.
    mask =
        builder->Or(mask, builder->Broadcast(builder->ConstantR0<bool>(false),
                                             input_shape.dim_sizes()));

    output = builder->Select(mask, output, input);
    context->SetOutput(0, output);
  }

 private:
  int32 batch_dim_;
  int32 seq_dim_;
};

REGISTER_XLA_OP(Name("ReverseSequence"), ReverseSequenceOp);

}  // namespace
}  // namespace tensorflow
<|endoftext|>


<commit_before>/*
 * Software License Agreement (BSD License)
 *
 *  Point Cloud Library (PCL) - www.pointclouds.org
 *  Copyright (c) 2010-2012, Willow Garage, Inc.
 *
 *  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 Willow Garage, Inc. 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 PCL_FEATURES_IMPL_ORGANIZED_EDGE_DETECTION_H_
#define PCL_FEATURES_IMPL_ORGANIZED_EDGE_DETECTION_H_

#include <pcl/features/organized_edge_detection.h>

/**
 *  Directions: 1 2 3
 *              0 x 4
 *              7 6 5
 * e.g. direction y means we came from pixel with label y to the center pixel x
 */
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template<typename PointT, typename PointLT> void
pcl::OrganizedEdgeDetection<PointT, PointLT>::compute (pcl::PointCloud<PointLT>& labels, std::vector<pcl::PointIndices>& label_indices) const
{
  const unsigned invalid_label = std::numeric_limits<unsigned>::max ();
  pcl::Label invalid_pt;
  invalid_pt.label = std::numeric_limits<unsigned>::max ();
  labels.points.resize (input_->points.size (), invalid_pt);
  labels.width = input_->width;
  labels.height = input_->height;
  unsigned int clust_id = 0;

  std::cout << "width: " << labels.width << std::endl;
  std::cout << "height: " << labels.height << std::endl;
  std::cout << "[done] OrganizedEdgeDetection::compute ()" << std::endl;

  // fill lookup table for next points to visit
  const int num_of_ngbr = 8;
  Neighbor directions [num_of_ngbr] = {Neighbor(-1,  0,                -1),
                                       Neighbor(-1, -1, -labels.width - 1),
                                       Neighbor( 0, -1, -labels.width    ),
                                       Neighbor( 1, -1, -labels.width + 1),
                                       Neighbor( 1,  0,                 1),
                                       Neighbor( 1,  1,  labels.width + 1),
                                       Neighbor( 0,  1,  labels.width    ),
                                       Neighbor(-1,  1,  labels.width - 1)};

  for (int row = 1; row < int(input_->height) - 1; row++)
  {
    for (int col = 1; col < int(input_->width) - 1; col++)
    {
      int curr_idx = row*int(input_->width) + col;
      if (!pcl_isfinite (input_->points[curr_idx].z))
        continue;

      float curr_depth = abs (input_->points[curr_idx].z);

      // Calculate depth distances between current point and neighboring points
      std::vector<float> nghr_dist;
      nghr_dist.resize (8);
      bool found_invalid_neighbor = false;
      for (int d_idx = 0; d_idx < num_of_ngbr; d_idx++)
      {
        int nghr_idx = curr_idx + directions[d_idx].d_index;
        assert (nghr_idx >= 0 && nghr_idx < input_->points.size ());
        if (!pcl_isfinite (input_->points[nghr_idx].z))
        {
          found_invalid_neighbor = true;
          break;
        }
        nghr_dist[d_idx] = curr_depth - abs (input_->points[nghr_idx].z);
      }

      if (!found_invalid_neighbor)
      {
        // Every neighboring points are valid
        std::pair<std::vector<float>::iterator, std::vector<float>::iterator> minmax;
        minmax = std::minmax_element (nghr_dist.begin (), nghr_dist.end ());
        float nghr_dist_min = *(minmax.first);
        float nghr_dist_max = *(minmax.second);
        float dist_dominant = abs (nghr_dist_min) > abs (nghr_dist_max) ? nghr_dist_min : nghr_dist_max;
        if (abs (dist_dominant) > th_depth_discon_*abs (curr_depth))
        {
          // Found a depth discontinuity
          if (dist_dominant > 0.f)
            labels[curr_idx].label = EDGELABEL_OCCLUDED;
          else
            labels[curr_idx].label = EDGELABEL_OCCLUDING;
        }
      }
      else
      {
        // Some neighboring points are not valid (nan points)
        // Search for corresponding point across invalid points
        // Search direction is determined by nan point locations with respect to current point
        int dx = 0;
        int dy = 0;
        int num_of_invalid_pt = 0;
        for (int d_idx = 0; d_idx < num_of_ngbr; d_idx++)
        {
          int nghr_idx = curr_idx + directions[d_idx].d_index;
          assert (nghr_idx >= 0 && nghr_idx < input_->points.size ());
          if (!pcl_isfinite (input_->points[nghr_idx].z))
          {
            dx += directions[d_idx].d_x;
            dy += directions[d_idx].d_y;
            num_of_invalid_pt++;
          }
        }

        // Search directions
        assert (num_of_invalid_pt > 0);
        float f_dx = static_cast<float> (dx) / static_cast<float> (num_of_invalid_pt);
        float f_dy = static_cast<float> (dy) / static_cast<float> (num_of_invalid_pt);

        // Search for corresponding point across invalid points
        float corr_depth = std::numeric_limits<float>::quiet_NaN ();
        for (int s_idx = 1; s_idx < max_search_neighbors_; s_idx++)
        {
          int s_row = row + static_cast<int> (std::floor (f_dy*static_cast<float> (s_idx)));
          int s_col = col + static_cast<int> (std::floor (f_dx*static_cast<float> (s_idx)));

          if (s_row < 0 || s_row >= int(input_->height) || s_col < 0 || s_col >= int(input_->width))
            break;

          if (pcl_isfinite (input_->points[s_row*int(input_->width)+s_col].z))
          {
            corr_depth = abs (input_->points[s_row*int(input_->width)+s_col].z);
            break;
          }
        }

        if (!pcl_isnan (corr_depth))
        {
          // Found a corresponding point
          float dist = curr_depth - corr_depth;
          if (abs (dist) > th_depth_discon_*abs (curr_depth))
          {
            // Found a depth discontinuity
            if (dist > 0.f)
              labels[curr_idx].label = EDGELABEL_OCCLUDED;
            else
              labels[curr_idx].label = EDGELABEL_OCCLUDING;
          }
        }
        else
        {
          // Not found a corresponding point, just nan boundary edge
          labels[curr_idx].label = EDGELABEL_NAN_BOUNDARY;
        }
      }
    }
  }

  // Assign label indices
  label_indices.resize (3);
  for (unsigned idx = 0; idx < input_->points.size (); idx++)
  {
    if (labels[idx].label != invalid_label)
    {
      label_indices[labels[idx].label].indices.push_back (idx);
    }
  }
}

#define PCL_INSTANTIATE_OrganizedEdgeDetection(T,LT) template class PCL_EXPORTS pcl::OrganizedEdgeDetection<T,LT>;

#endif //#ifndef PCL_FEATURES_IMPL_ORGANIZED_EDGE_DETECTION_H_
<commit_msg>Modified organized_edge_detection to remove minmax_element, in case we don't have c++11<commit_after>/*
 * Software License Agreement (BSD License)
 *
 *  Point Cloud Library (PCL) - www.pointclouds.org
 *  Copyright (c) 2010-2012, Willow Garage, Inc.
 *
 *  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 Willow Garage, Inc. 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 PCL_FEATURES_IMPL_ORGANIZED_EDGE_DETECTION_H_
#define PCL_FEATURES_IMPL_ORGANIZED_EDGE_DETECTION_H_

#include <pcl/features/organized_edge_detection.h>

/**
 *  Directions: 1 2 3
 *              0 x 4
 *              7 6 5
 * e.g. direction y means we came from pixel with label y to the center pixel x
 */
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template<typename PointT, typename PointLT> void
pcl::OrganizedEdgeDetection<PointT, PointLT>::compute (pcl::PointCloud<PointLT>& labels, std::vector<pcl::PointIndices>& label_indices) const
{
  const unsigned invalid_label = std::numeric_limits<unsigned>::max ();
  pcl::Label invalid_pt;
  invalid_pt.label = std::numeric_limits<unsigned>::max ();
  labels.points.resize (input_->points.size (), invalid_pt);
  labels.width = input_->width;
  labels.height = input_->height;
  unsigned int clust_id = 0;

  std::cout << "width: " << labels.width << std::endl;
  std::cout << "height: " << labels.height << std::endl;
  std::cout << "[done] OrganizedEdgeDetection::compute ()" << std::endl;

  // fill lookup table for next points to visit
  const int num_of_ngbr = 8;
  Neighbor directions [num_of_ngbr] = {Neighbor(-1,  0,                -1),
                                       Neighbor(-1, -1, -labels.width - 1),
                                       Neighbor( 0, -1, -labels.width    ),
                                       Neighbor( 1, -1, -labels.width + 1),
                                       Neighbor( 1,  0,                 1),
                                       Neighbor( 1,  1,  labels.width + 1),
                                       Neighbor( 0,  1,  labels.width    ),
                                       Neighbor(-1,  1,  labels.width - 1)};

  for (int row = 1; row < int(input_->height) - 1; row++)
  {
    for (int col = 1; col < int(input_->width) - 1; col++)
    {
      int curr_idx = row*int(input_->width) + col;
      if (!pcl_isfinite (input_->points[curr_idx].z))
        continue;

      float curr_depth = abs (input_->points[curr_idx].z);

      // Calculate depth distances between current point and neighboring points
      std::vector<float> nghr_dist;
      nghr_dist.resize (8);
      bool found_invalid_neighbor = false;
      for (int d_idx = 0; d_idx < num_of_ngbr; d_idx++)
      {
        int nghr_idx = curr_idx + directions[d_idx].d_index;
        assert (nghr_idx >= 0 && nghr_idx < input_->points.size ());
        if (!pcl_isfinite (input_->points[nghr_idx].z))
        {
          found_invalid_neighbor = true;
          break;
        }
        nghr_dist[d_idx] = curr_depth - abs (input_->points[nghr_idx].z);
      }

      if (!found_invalid_neighbor)
      {
        // Every neighboring points are valid
        std::pair<std::vector<float>::iterator, std::vector<float>::iterator> minmax;
        //minmax = boost::minmax_element (nghr_dist.begin (), nghr_dist.end ());
        std::vector<float>::iterator min_itr = std::min_element (nghr_dist.begin (), nghr_dist.end ());//*(minmax.first);
        std::vector<float>::iterator max_itr = std::max_element (nghr_dist.begin (), nghr_dist.end ());//*(minmax.first);
        float nghr_dist_min = *min_itr;
        float nghr_dist_max = *max_itr;
        float dist_dominant = abs (nghr_dist_min) > abs (nghr_dist_max) ? nghr_dist_min : nghr_dist_max;
        if (abs (dist_dominant) > th_depth_discon_*abs (curr_depth))
        {
          // Found a depth discontinuity
          if (dist_dominant > 0.f)
            labels[curr_idx].label = EDGELABEL_OCCLUDED;
          else
            labels[curr_idx].label = EDGELABEL_OCCLUDING;
        }
      }
      else
      {
        // Some neighboring points are not valid (nan points)
        // Search for corresponding point across invalid points
        // Search direction is determined by nan point locations with respect to current point
        int dx = 0;
        int dy = 0;
        int num_of_invalid_pt = 0;
        for (int d_idx = 0; d_idx < num_of_ngbr; d_idx++)
        {
          int nghr_idx = curr_idx + directions[d_idx].d_index;
          assert (nghr_idx >= 0 && nghr_idx < input_->points.size ());
          if (!pcl_isfinite (input_->points[nghr_idx].z))
          {
            dx += directions[d_idx].d_x;
            dy += directions[d_idx].d_y;
            num_of_invalid_pt++;
          }
        }

        // Search directions
        assert (num_of_invalid_pt > 0);
        float f_dx = static_cast<float> (dx) / static_cast<float> (num_of_invalid_pt);
        float f_dy = static_cast<float> (dy) / static_cast<float> (num_of_invalid_pt);

        // Search for corresponding point across invalid points
        float corr_depth = std::numeric_limits<float>::quiet_NaN ();
        for (int s_idx = 1; s_idx < max_search_neighbors_; s_idx++)
        {
          int s_row = row + static_cast<int> (std::floor (f_dy*static_cast<float> (s_idx)));
          int s_col = col + static_cast<int> (std::floor (f_dx*static_cast<float> (s_idx)));

          if (s_row < 0 || s_row >= int(input_->height) || s_col < 0 || s_col >= int(input_->width))
            break;

          if (pcl_isfinite (input_->points[s_row*int(input_->width)+s_col].z))
          {
            corr_depth = abs (input_->points[s_row*int(input_->width)+s_col].z);
            break;
          }
        }

        if (!pcl_isnan (corr_depth))
        {
          // Found a corresponding point
          float dist = curr_depth - corr_depth;
          if (abs (dist) > th_depth_discon_*abs (curr_depth))
          {
            // Found a depth discontinuity
            if (dist > 0.f)
              labels[curr_idx].label = EDGELABEL_OCCLUDED;
            else
              labels[curr_idx].label = EDGELABEL_OCCLUDING;
          }
        }
        else
        {
          // Not found a corresponding point, just nan boundary edge
          labels[curr_idx].label = EDGELABEL_NAN_BOUNDARY;
        }
      }
    }
  }

  // Assign label indices
  label_indices.resize (3);
  for (unsigned idx = 0; idx < input_->points.size (); idx++)
  {
    if (labels[idx].label != invalid_label)
    {
      label_indices[labels[idx].label].indices.push_back (idx);
    }
  }
}

#define PCL_INSTANTIATE_OrganizedEdgeDetection(T,LT) template class PCL_EXPORTS pcl::OrganizedEdgeDetection<T,LT>;

#endif //#ifndef PCL_FEATURES_IMPL_ORGANIZED_EDGE_DETECTION_H_
<|endoftext|>


<commit_before>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2014.  All rights reserved
 */
#ifndef _Stroika_Foundation_Memory_SmallStackBuffer_inl_
#define _Stroika_Foundation_Memory_SmallStackBuffer_inl_    1

/*
 ********************************************************************************
 ***************************** Implementation Details ***************************
 ********************************************************************************
 */
#include    <algorithm>
#include    <cstring>
#include    <type_traits>

#include    "../Debug/Assertions.h"
#include    "Common.h"
#include    "LeakChecker.h"

namespace   Stroika {
    namespace   Foundation {
        namespace   Memory {

            /*
             ********************************************************************************
             ************************* SmallStackBuffer<T, BUF_SIZE> ************************
             ********************************************************************************
             */
            template    <typename   T, size_t BUF_SIZE>
            inline  void    SmallStackBuffer<T, BUF_SIZE>::GrowToSize (size_t nElements)
            {
                fSize_ = nElements;
                // very rare we'll need to grow past this limit. And we want to keep this routine small so it can be
                // inlined. And put the rare, complex logic in other outofline function
                if (nElements > (NEltsOf (fBuffer_))) {
                    GrowToSize_ (nElements);
                }
            }
            template    <typename   T, size_t BUF_SIZE>
            void    SmallStackBuffer<T, BUF_SIZE>::GrowToSize_ (size_t nElements)
            {
                Require (nElements > (NEltsOf (fBuffer_)));
                // if we were using buffer, then assume whole thing, and if we malloced, save
                // size in unused buffer
                Assert (sizeof (fBuffer_) >= sizeof (size_t));   // one customer changes the size of the buffer to 1, and wondered why it crashed...
                size_t  oldEltCount =   (fPointer_ == fBuffer_) ?
                                        NEltsOf (fBuffer_) :
                                        *(size_t*)&fBuffer_;
                if (nElements > oldEltCount) {
                    /*
                    *   If we REALLY must grow, the double in size so unlikely we'll have to grow/malloc/copy again.
                    */
                    nElements = max (nElements, oldEltCount * 2);

                    T*  newPtr = DEBUG_NEW T [nElements];           // NB: We are careful not to update our size field til this has succeeded (exception safety)

                    // Not totally safe for T with CTOR/DTOR/Op= ... Don't use this class in that case!!!
                    // No idea how many to copy!!! - do worst case(maybe should keep old size if this ever
                    // bus errors???)
                    (void)::memcpy (newPtr, fPointer_, oldEltCount * sizeof (T));
                    if (fPointer_ != fBuffer_) {
                        // we must have used the heap...
                        delete[] fPointer_;
                    }
                    fPointer_ = newPtr;

                    // since we are using the heap, we can store the size in our fBuffer_
                    *(size_t*)&fBuffer_ = nElements;
                }
            }
            template    <typename   T, size_t BUF_SIZE>
            inline  SmallStackBuffer<T, BUF_SIZE>::SmallStackBuffer (size_t nElements)
                : fSize_ (0)
                //, fBuffer_ ()
                , fPointer_ (fBuffer_)
            {
#if     qDebug
                memcpy (fGuard1_, kGuard1_, sizeof (kGuard1_));
                memcpy (fGuard2_, kGuard2_, sizeof (kGuard2_));
#endif
#if     !qCompilerAndStdLib_TypeTraitsNewNamesIsCopyableEtc_Buggy
                static_assert(std::is_trivially_constructible<T>::value, "require T is is_trivially_constructible");
                static_assert(std::is_trivially_destructible<T>::value, "require T is is_trivially_destructible");
                static_assert(std::is_trivially_copyable<T>::value, "require T is is_trivially_copyable");
#endif
                GrowToSize (nElements);
            }
            template    <typename   T, size_t BUF_SIZE>
            SmallStackBuffer<T, BUF_SIZE>::SmallStackBuffer (const SmallStackBuffer<T, BUF_SIZE>& from)
                : fSize_ (0)
                //, fBuffer_ (),
                , fPointer_ (fBuffer_)
            {
#if     !qCompilerAndStdLib_TypeTraitsNewNamesIsCopyableEtc_Buggy
                static_assert(std::is_trivially_constructible<T>::value, "require T is is_trivially_constructible");
                static_assert(std::is_trivially_destructible<T>::value, "require T is is_trivially_destructible");
                static_assert(std::is_trivially_copyable<T>::value, "require T is is_trivially_copyable");
#endif
                GrowToSize (from.fSize_);
#if     qSilenceAnnoyingCompilerWarnings && _MSC_VER
                Memory::Private::VC_BWA_std_copy (from.fPointer_, from.fPointer_ + from.fSize_, fPointer_);
#else
                std::copy (from.fPointer_, from.fPointer_ + from.fSize_, fPointer_);
#endif
            }
            template    <typename   T, size_t BUF_SIZE>
            inline  SmallStackBuffer<T, BUF_SIZE>::~SmallStackBuffer ()
            {
#if     qDebug
                ValidateGuards_ ();
#endif
                if (fPointer_ != fBuffer_) {
                    // we must have used the heap...
                    delete[] fPointer_;
                }
            }
            template    <typename   T, size_t BUF_SIZE>
            SmallStackBuffer<T, BUF_SIZE>&   SmallStackBuffer<T, BUF_SIZE>::operator= (const SmallStackBuffer<T, BUF_SIZE>& rhs)
            {
#if     qDebug
                ValidateGuards_ ();
#endif
                GrowToSize (rhs.fSize_);
#if     qSilenceAnnoyingCompilerWarnings && _MSC_VER
                Memory::Private::VC_BWA_std_copy (rhs.fPointer_, rhs.fPointer_ + rhs.fSize_, fPointer_);
#else
                std::copy (rhs.fPointer_, rhs.fPointer_ + rhs.fSize_, fPointer_);
#endif
                return *this;
            }
            template    <typename   T, size_t BUF_SIZE>
            inline  typename SmallStackBuffer<T, BUF_SIZE>::iterator     SmallStackBuffer<T, BUF_SIZE>::begin ()
            {
                return fPointer_;
            }
            template    <typename   T, size_t BUF_SIZE>
            inline  typename SmallStackBuffer<T, BUF_SIZE>::iterator     SmallStackBuffer<T, BUF_SIZE>::end ()
            {
                return fPointer_ + fSize_;
            }
            template    <typename   T, size_t BUF_SIZE>
            inline  typename SmallStackBuffer<T, BUF_SIZE>::const_iterator   SmallStackBuffer<T, BUF_SIZE>::begin () const
            {
                return fPointer_;
            }
            template    <typename   T, size_t BUF_SIZE>
            inline  typename SmallStackBuffer<T, BUF_SIZE>::const_iterator   SmallStackBuffer<T, BUF_SIZE>::end () const
            {
                return fPointer_ + fSize_;
            }
            template    <typename   T, size_t BUF_SIZE>
            inline  size_t  SmallStackBuffer<T, BUF_SIZE>::GetSize () const
            {
                return fSize_;
            }
            template    <typename   T, size_t BUF_SIZE>
            inline  void    SmallStackBuffer<T, BUF_SIZE>::push_back (const T& e)
            {
                size_t  s   =   GetSize ();
                GrowToSize (s + 1);
                fPointer_[s] = e;
            }
            template    <typename   T, size_t BUF_SIZE>
            inline  SmallStackBuffer<T, BUF_SIZE>::operator T* ()
            {
                AssertNotNull (fPointer_);
                return (fPointer_);
            }
            template    <typename   T, size_t BUF_SIZE>
            inline  SmallStackBuffer<T, BUF_SIZE>::operator const T* () const
            {
                AssertNotNull (fPointer_);
                return (fPointer_);
            }
#if     qDebug
#if     qCompilerAndStdLib_constexpr_Buggy
            template    <typename   T, size_t BUF_SIZE>
            constexpr   Byte    SmallStackBuffer<T, BUF_SIZE>::kGuard1_[8]  =   { 0x45, 0x23, 0x12, 0x56, 0x99, 0x76, 0x12, 0x55, };
            template    <typename   T, size_t BUF_SIZE>
            constexpr   Byte    SmallStackBuffer<T, BUF_SIZE>::kGuard2_[8]  =   { 0x15, 0x32, 0xa5, 0x16, 0x15, 0x7a, 0x90, 0x10, };
#endif
            template    <typename   T, size_t BUF_SIZE>
            void    SmallStackBuffer<T, BUF_SIZE>::ValidateGuards_ ()
            {
                Assert (memcmp (kGuard1_, fGuard1_, sizeof (kGuard1_)) == 0);
                Assert (memcmp (kGuard2_, fGuard2_, sizeof (kGuard2_)) == 0);
            }
#endif

        }
    }
}
#endif  /*_Stroika_Foundation_Memory_SmallStackBuffer_inl_*/
<commit_msg>workaround qCompilerAndStdLib_constexpr_StaticDataMember_Buggy for SmallStackBuffer<> guard template<commit_after>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2014.  All rights reserved
 */
#ifndef _Stroika_Foundation_Memory_SmallStackBuffer_inl_
#define _Stroika_Foundation_Memory_SmallStackBuffer_inl_    1

/*
 ********************************************************************************
 ***************************** Implementation Details ***************************
 ********************************************************************************
 */
#include    <algorithm>
#include    <cstring>
#include    <type_traits>

#include    "../Debug/Assertions.h"
#include    "Common.h"
#include    "LeakChecker.h"

namespace   Stroika {
    namespace   Foundation {
        namespace   Memory {

            /*
             ********************************************************************************
             ************************* SmallStackBuffer<T, BUF_SIZE> ************************
             ********************************************************************************
             */
            template    <typename   T, size_t BUF_SIZE>
            inline  void    SmallStackBuffer<T, BUF_SIZE>::GrowToSize (size_t nElements)
            {
                fSize_ = nElements;
                // very rare we'll need to grow past this limit. And we want to keep this routine small so it can be
                // inlined. And put the rare, complex logic in other outofline function
                if (nElements > (NEltsOf (fBuffer_))) {
                    GrowToSize_ (nElements);
                }
            }
            template    <typename   T, size_t BUF_SIZE>
            void    SmallStackBuffer<T, BUF_SIZE>::GrowToSize_ (size_t nElements)
            {
                Require (nElements > (NEltsOf (fBuffer_)));
                // if we were using buffer, then assume whole thing, and if we malloced, save
                // size in unused buffer
                Assert (sizeof (fBuffer_) >= sizeof (size_t));   // one customer changes the size of the buffer to 1, and wondered why it crashed...
                size_t  oldEltCount =   (fPointer_ == fBuffer_) ?
                                        NEltsOf (fBuffer_) :
                                        *(size_t*)&fBuffer_;
                if (nElements > oldEltCount) {
                    /*
                    *   If we REALLY must grow, the double in size so unlikely we'll have to grow/malloc/copy again.
                    */
                    nElements = max (nElements, oldEltCount * 2);

                    T*  newPtr = DEBUG_NEW T [nElements];           // NB: We are careful not to update our size field til this has succeeded (exception safety)

                    // Not totally safe for T with CTOR/DTOR/Op= ... Don't use this class in that case!!!
                    // No idea how many to copy!!! - do worst case(maybe should keep old size if this ever
                    // bus errors???)
                    (void)::memcpy (newPtr, fPointer_, oldEltCount * sizeof (T));
                    if (fPointer_ != fBuffer_) {
                        // we must have used the heap...
                        delete[] fPointer_;
                    }
                    fPointer_ = newPtr;

                    // since we are using the heap, we can store the size in our fBuffer_
                    *(size_t*)&fBuffer_ = nElements;
                }
            }
            template    <typename   T, size_t BUF_SIZE>
            inline  SmallStackBuffer<T, BUF_SIZE>::SmallStackBuffer (size_t nElements)
                : fSize_ (0)
                //, fBuffer_ ()
                , fPointer_ (fBuffer_)
            {
#if     qDebug
                memcpy (fGuard1_, kGuard1_, sizeof (kGuard1_));
                memcpy (fGuard2_, kGuard2_, sizeof (kGuard2_));
#endif
#if     !qCompilerAndStdLib_TypeTraitsNewNamesIsCopyableEtc_Buggy
                static_assert(std::is_trivially_constructible<T>::value, "require T is is_trivially_constructible");
                static_assert(std::is_trivially_destructible<T>::value, "require T is is_trivially_destructible");
                static_assert(std::is_trivially_copyable<T>::value, "require T is is_trivially_copyable");
#endif
                GrowToSize (nElements);
            }
            template    <typename   T, size_t BUF_SIZE>
            SmallStackBuffer<T, BUF_SIZE>::SmallStackBuffer (const SmallStackBuffer<T, BUF_SIZE>& from)
                : fSize_ (0)
                //, fBuffer_ (),
                , fPointer_ (fBuffer_)
            {
#if     !qCompilerAndStdLib_TypeTraitsNewNamesIsCopyableEtc_Buggy
                static_assert(std::is_trivially_constructible<T>::value, "require T is is_trivially_constructible");
                static_assert(std::is_trivially_destructible<T>::value, "require T is is_trivially_destructible");
                static_assert(std::is_trivially_copyable<T>::value, "require T is is_trivially_copyable");
#endif
                GrowToSize (from.fSize_);
#if     qSilenceAnnoyingCompilerWarnings && _MSC_VER
                Memory::Private::VC_BWA_std_copy (from.fPointer_, from.fPointer_ + from.fSize_, fPointer_);
#else
                std::copy (from.fPointer_, from.fPointer_ + from.fSize_, fPointer_);
#endif
            }
            template    <typename   T, size_t BUF_SIZE>
            inline  SmallStackBuffer<T, BUF_SIZE>::~SmallStackBuffer ()
            {
#if     qDebug
                ValidateGuards_ ();
#endif
                if (fPointer_ != fBuffer_) {
                    // we must have used the heap...
                    delete[] fPointer_;
                }
            }
            template    <typename   T, size_t BUF_SIZE>
            SmallStackBuffer<T, BUF_SIZE>&   SmallStackBuffer<T, BUF_SIZE>::operator= (const SmallStackBuffer<T, BUF_SIZE>& rhs)
            {
#if     qDebug
                ValidateGuards_ ();
#endif
                GrowToSize (rhs.fSize_);
#if     qSilenceAnnoyingCompilerWarnings && _MSC_VER
                Memory::Private::VC_BWA_std_copy (rhs.fPointer_, rhs.fPointer_ + rhs.fSize_, fPointer_);
#else
                std::copy (rhs.fPointer_, rhs.fPointer_ + rhs.fSize_, fPointer_);
#endif
                return *this;
            }
            template    <typename   T, size_t BUF_SIZE>
            inline  typename SmallStackBuffer<T, BUF_SIZE>::iterator     SmallStackBuffer<T, BUF_SIZE>::begin ()
            {
                return fPointer_;
            }
            template    <typename   T, size_t BUF_SIZE>
            inline  typename SmallStackBuffer<T, BUF_SIZE>::iterator     SmallStackBuffer<T, BUF_SIZE>::end ()
            {
                return fPointer_ + fSize_;
            }
            template    <typename   T, size_t BUF_SIZE>
            inline  typename SmallStackBuffer<T, BUF_SIZE>::const_iterator   SmallStackBuffer<T, BUF_SIZE>::begin () const
            {
                return fPointer_;
            }
            template    <typename   T, size_t BUF_SIZE>
            inline  typename SmallStackBuffer<T, BUF_SIZE>::const_iterator   SmallStackBuffer<T, BUF_SIZE>::end () const
            {
                return fPointer_ + fSize_;
            }
            template    <typename   T, size_t BUF_SIZE>
            inline  size_t  SmallStackBuffer<T, BUF_SIZE>::GetSize () const
            {
                return fSize_;
            }
            template    <typename   T, size_t BUF_SIZE>
            inline  void    SmallStackBuffer<T, BUF_SIZE>::push_back (const T& e)
            {
                size_t  s   =   GetSize ();
                GrowToSize (s + 1);
                fPointer_[s] = e;
            }
            template    <typename   T, size_t BUF_SIZE>
            inline  SmallStackBuffer<T, BUF_SIZE>::operator T* ()
            {
                AssertNotNull (fPointer_);
                return (fPointer_);
            }
            template    <typename   T, size_t BUF_SIZE>
            inline  SmallStackBuffer<T, BUF_SIZE>::operator const T* () const
            {
                AssertNotNull (fPointer_);
                return (fPointer_);
            }
#if     qDebug
#if     qCompilerAndStdLib_constexpr_Buggy
            template    <typename   T, size_t BUF_SIZE>
            constexpr   Byte    SmallStackBuffer<T, BUF_SIZE>::kGuard1_[8]  =   { 0x45, 0x23, 0x12, 0x56, 0x99, 0x76, 0x12, 0x55, };
            template    <typename   T, size_t BUF_SIZE>
            constexpr   Byte    SmallStackBuffer<T, BUF_SIZE>::kGuard2_[8]  =   { 0x15, 0x32, 0xa5, 0x16, 0x15, 0x7a, 0x90, 0x10, };
#elif   qCompilerAndStdLib_constexpr_StaticDataMember_Buggy
            template    <typename   T, size_t BUF_SIZE>
            constexpr   Byte    SmallStackBuffer<T, BUF_SIZE>::kGuard1_[8];
            template    <typename   T, size_t BUF_SIZE>
            constexpr   Byte    SmallStackBuffer<T, BUF_SIZE>::kGuard2_[8];
#endif
            template    <typename   T, size_t BUF_SIZE>
            void    SmallStackBuffer<T, BUF_SIZE>::ValidateGuards_ ()
            {
                Assert (memcmp (kGuard1_, fGuard1_, sizeof (kGuard1_)) == 0);
                Assert (memcmp (kGuard2_, fGuard2_, sizeof (kGuard2_)) == 0);
            }
#endif

        }
    }
}
#endif  /*_Stroika_Foundation_Memory_SmallStackBuffer_inl_*/
<|endoftext|>