<commit_before>// @(#)root/thread:$Id$
// Author: Danilo Piparo, CERN  11/2/2016

/*************************************************************************
 * Copyright (C) 1995-2018, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

#ifndef ROOT_TThreadedObject
#define ROOT_TThreadedObject

#include "TList.h"
#include "TError.h"

#include <functional>
#include <map>
#include <memory>
#include <mutex>
#include <string>
#include <thread>
#include <vector>

#include "ROOT/TSpinMutex.hxx"
#include "TROOT.h"

class TH1;

namespace ROOT {

   namespace Internal {

      namespace TThreadedObjectUtils {

         /// Get the unique index identifying a TThreadedObject.
         inline unsigned GetTThreadedObjectIndex() {
            static unsigned fgTThreadedObjectIndex = 0;
            return fgTThreadedObjectIndex++;
         }

         template<typename T, bool ISHISTO = std::is_base_of<TH1,T>::value>
         struct Detacher{
            static T* Detach(T* obj) {
               return obj;
            }
         };

         template<typename T>
         struct Detacher<T, true>{
            static T* Detach(T* obj) {
               obj->SetDirectory(nullptr);
               obj->ResetBit(kMustCleanup);
               return obj;
            }
         };

         /// Return a copy of the object or a "Clone" if the copy constructor is not implemented.
         template<class T, bool isCopyConstructible = std::is_copy_constructible<T>::value>
         struct Cloner {
            static T *Clone(const T *obj, TDirectory* d = nullptr) {
               T* clone;
               if (d){
                  TDirectory::TContext ctxt(d);
                  clone = new T(*obj);
               } else {
                  clone = new T(*obj);
               }
               return Detacher<T>::Detach(clone);
            }
         };

         template<class T>
         struct Cloner<T, false> {
            static T *Clone(const T *obj, TDirectory* d = nullptr) {
               T* clone;
               if (d){
                  TDirectory::TContext ctxt(d);
                  clone = (T*)obj->Clone();
               } else {
                  clone = (T*)obj->Clone();
               }
               return clone;
            }
         };

         template<class T, bool ISHISTO = std::is_base_of<TH1,T>::value>
         struct DirCreator{
            static std::vector<TDirectory*> Create(unsigned maxSlots) {
               std::string dirName = "__TThreaded_dir_";
               dirName += std::to_string(ROOT::Internal::TThreadedObjectUtils::GetTThreadedObjectIndex()) + "_";
               std::vector<TDirectory*> dirs;
               dirs.reserve(maxSlots);
               for (unsigned i=0; i< maxSlots;++i) {
                  auto dir = gROOT->mkdir((dirName+std::to_string(i)).c_str());
                  dirs.emplace_back(dir);
               }
               return dirs;
            }
         };

         template<class T>
         struct DirCreator<T, true>{
            static std::vector<TDirectory*> Create(unsigned maxSlots) {
               std::vector<TDirectory*> dirs(maxSlots, nullptr);
               return dirs;
            }
         };
      } // End of namespace TThreadedObjectUtils
   } // End of namespace Internal

   namespace TThreadedObjectUtils {

      template<class T>
      using MergeFunctionType = std::function<void(std::shared_ptr<T>, std::vector<std::shared_ptr<T>>&)>;
      /// Merge TObjects
      template<class T>
      void MergeTObjects(std::shared_ptr<T> target, std::vector<std::shared_ptr<T>> &objs)
      {
         if (!target) return;
         TList objTList;
         // Cannot do better than this
         for (auto obj : objs) {
            if (obj && obj != target) objTList.Add(obj.get());
         }
         target->Merge(&objTList);
      }
   } // end of namespace TThreadedObjectUtils

   /**
    * \class ROOT::TThreadedObject
    * \brief A wrapper to make object instances thread private, lazily.
    * \tparam T Class of the object to be made thread private (e.g. TH1F)
    * \ingroup Multicore
    *
    * A wrapper which makes objects thread private. The methods of the underlying
    * object can be invoked via the the arrow operator. The object is created in
    * a specific thread lazily, i.e. upon invocation of one of its methods.
    * The correct object pointer from within a particular thread can be accessed
    * with the overloaded arrow operator or with the Get method.
    * In case an elaborate thread management is in place, e.g. in presence of
    * stream of operations or "processing slots", it is also possible to
    * manually select the correct object pointer explicitly.
    * The default size of the threaded objects is 64. This size can be extended
    * manually via the fgMaxSlots parameter. The size of individual instances
    * is automatically extended if the size of the implicit MT pool is bigger
    * than 64.
    *
    */
   template<class T>
   class TThreadedObject {
   public:
      static unsigned fgMaxSlots; ///< The maximum number of processing slots (distinct threads) which the instances can manage
      TThreadedObject(const TThreadedObject&) = delete;
      /// Construct the TThreaded object and the "model" of the thread private
      /// objects.
      /// \tparam ARGS Arguments of the constructor of T
      template<class ...ARGS>
      TThreadedObject(ARGS&&... args)
      {
         const auto imtPoolSize = ROOT::GetImplicitMTPoolSize();
         fMaxSlots = (64 > imtPoolSize) ? fgMaxSlots : imtPoolSize;
         fObjPointers = std::vector<std::shared_ptr<T>>(fMaxSlots, nullptr);
         fDirectories = Internal::TThreadedObjectUtils::DirCreator<T>::Create(fMaxSlots);

         TDirectory::TContext ctxt(fDirectories[0]);
         fModel.reset(Internal::TThreadedObjectUtils::Detacher<T>::Detach(new T(std::forward<ARGS>(args)...)));
      }

      /// Access a particular processing slot. This
      /// method is *thread-unsafe*: it cannot be invoked from two different
      /// threads with the same argument.
      std::shared_ptr<T> GetAtSlot(unsigned i)
      {
         if ( i >= fObjPointers.size()) {
            Warning("TThreadedObject::GetAtSlot", "Maximum number of slots reached.");
            return nullptr;
         }
         auto objPointer = fObjPointers[i];
         if (!objPointer) {
            objPointer.reset(Internal::TThreadedObjectUtils::Cloner<T>::Clone(fModel.get(), fDirectories[i]));
            fObjPointers[i] = objPointer;
         }
         return objPointer;
      }

      /// Set the value of a particular slot.
      void SetAtSlot(unsigned i, std::shared_ptr<T> v)
      {
         fObjPointers[i] = v;
      }

      /// Access a particular slot which corresponds to a single thread.
      /// This is in general faster than the GetAtSlot method but it is
      /// responsibility of the caller to make sure that an object is
      /// initialised for the particular slot.
      std::shared_ptr<T> GetAtSlotUnchecked(unsigned i) const
      {
         return fObjPointers[i];
      }

      /// Access a particular slot which corresponds to a single thread.
      /// This overload is faster than the GetAtSlotUnchecked method but
      /// the caller is responsible to make sure that an object is
      /// initialised for the particular slot and that the returned pointer
      /// will not outlive the TThreadedObject that returned it.
      T* GetAtSlotRaw(unsigned i) const
      {
         return fObjPointers[i].get();
      }

      /// Access the pointer corresponding to the current slot. This method is
      /// not adequate for being called inside tight loops as it implies a
      /// lookup in a mapping between the threadIDs and the slot indices.
      /// A good practice consists in copying the pointer onto the stack and
      /// proceed with the loop as shown in this work item (psudo-code) which
      /// will be sent to different threads:
      /// ~~~{.cpp}
      /// auto workItem = [](){
      ///    auto objPtr = tthreadedObject.Get();
      ///    for (auto i : ROOT::TSeqI(1000)) {
      ///       // tthreadedObject->FastMethod(i); // don't do this! Inefficient!
      ///       objPtr->FastMethod(i);
      ///    }
      /// }
      /// ~~~
      std::shared_ptr<T> Get()
      {
         return GetAtSlot(GetThisSlotNumber());
      }

      /// Access the wrapped object and allow to call its methods.
      T *operator->()
      {
         return Get().get();
      }

      /// Merge all the thread private objects. Can be called once: it does not
      /// create any new object but destroys the present bookkeping collapsing
      /// all objects into the one at slot 0.
      std::shared_ptr<T> Merge(TThreadedObjectUtils::MergeFunctionType<T> mergeFunction = TThreadedObjectUtils::MergeTObjects<T>)
      {
         // We do not return if we already merged.
         if (fIsMerged) {
            Warning("TThreadedObject::Merge", "This object was already merged. Returning the previous result.");
            return fObjPointers[0];
         }
         mergeFunction(fObjPointers[0], fObjPointers);
         fIsMerged = true;
         return fObjPointers[0];
      }

      /// Merge all the thread private objects. Can be called many times. It
      /// does create a new instance of class T to represent the "Sum" object.
      /// This method is not thread safe: correct or acceptable behaviours
      /// depend on the nature of T and of the merging function.
      std::unique_ptr<T> SnapshotMerge(TThreadedObjectUtils::MergeFunctionType<T> mergeFunction = TThreadedObjectUtils::MergeTObjects<T>)
      {
         if (fIsMerged) {
            Warning("TThreadedObject::SnapshotMerge", "This object was already merged. Returning the previous result.");
            return std::unique_ptr<T>(Internal::TThreadedObjectUtils::Cloner<T>::Clone(fObjPointers[0].get()));
         }
         auto targetPtr = Internal::TThreadedObjectUtils::Cloner<T>::Clone(fModel.get());
         std::shared_ptr<T> targetPtrShared(targetPtr, [](T *) {});
         mergeFunction(targetPtrShared, fObjPointers);
         return std::unique_ptr<T>(targetPtr);
      }

   private:
      unsigned fMaxSlots;                                ///< The size of the instance
      std::unique_ptr<T> fModel;                         ///< Use to store a "model" of the object
      std::vector<std::shared_ptr<T>> fObjPointers;      ///< A pointer per thread is kept.
      std::vector<TDirectory*> fDirectories;             ///< A TDirectory per thread is kept.
      std::map<std::thread::id, unsigned> fThrIDSlotMap; ///< A mapping between the thread IDs and the slots
      unsigned fCurrMaxSlotIndex = 0;                    ///< The maximum slot index
      bool fIsMerged = false;                            ///< Remember if the objects have been merged already
      ROOT::TSpinMutex fThrIDSlotMutex;                  ///< Mutex to protect the ID-slot map access

      /// Get the slot number for this threadID.
      unsigned GetThisSlotNumber()
      {
         const auto thisThreadID = std::this_thread::get_id();
         unsigned thisIndex;
         {
            std::lock_guard<ROOT::TSpinMutex> lg(fThrIDSlotMutex);
            auto thisSlotNumIt = fThrIDSlotMap.find(thisThreadID);
            if (thisSlotNumIt != fThrIDSlotMap.end()) return thisSlotNumIt->second;
            thisIndex = fCurrMaxSlotIndex++;
            fThrIDSlotMap[thisThreadID] = thisIndex;
         }
         return thisIndex;
      }

   };

   template<class T> unsigned TThreadedObject<T>::fgMaxSlots = 64;

} // End ROOT namespace

#include <sstream>

////////////////////////////////////////////////////////////////////////////////
/// Print a TThreadedObject at the prompt:

namespace cling {
   template<class T>
   std::string printValue(ROOT::TThreadedObject<T> *val)
   {
      auto model = ((std::unique_ptr<T>*)(val))->get();
      std::ostringstream ret;
      ret << "A wrapper to make object instances thread private, lazily. "
          << "The model which is replicated is " << printValue(model);
      return ret.str();
   }
}

#endif
<commit_msg>[thread] Provide TThreadedObject() taking nslots:<commit_after>// @(#)root/thread:$Id$
// Author: Danilo Piparo, CERN  11/2/2016

/*************************************************************************
 * Copyright (C) 1995-2018, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

#ifndef ROOT_TThreadedObject
#define ROOT_TThreadedObject

#include "ROOT/TSpinMutex.hxx"
#include "TDirectory.h"
#include "TError.h"
#include "TList.h"
#include "TROOT.h"

#include <algorithm>
#include <exception>
#include <functional>
#include <map>
#include <memory>
#include <mutex>
#include <string>
#include <thread>
#include <vector>

class TH1;

namespace ROOT {

   /**
    * \class ROOT::TNumSlots
    * \brief Defines the number of threads in some of ROOT's interfaces.
    */
   struct TNumSlots {
      int fVal; // number of slots
      friend bool operator==(TNumSlots lhs, TNumSlots rhs) { return lhs.fVal == rhs.fVal; }
      friend bool operator!=(TNumSlots lhs, TNumSlots rhs) { return lhs.fVal != rhs.fVal; }
   };

   static constexpr const TNumSlots kIMTPoolSize{-1}; // Whatever the IMT pool size is.

   namespace Internal {

      namespace TThreadedObjectUtils {

         /// Get the unique index identifying a TThreadedObject.
         inline unsigned GetTThreadedObjectIndex() {
            static unsigned fgTThreadedObjectIndex = 0;
            return fgTThreadedObjectIndex++;
         }

         template<typename T, bool ISHISTO = std::is_base_of<TH1,T>::value>
         struct Detacher{
            static T* Detach(T* obj) {
               return obj;
            }
         };

         template<typename T>
         struct Detacher<T, true>{
            static T* Detach(T* obj) {
               obj->SetDirectory(nullptr);
               obj->ResetBit(kMustCleanup);
               return obj;
            }
         };

         /// Return a copy of the object or a "Clone" if the copy constructor is not implemented.
         template<class T, bool isCopyConstructible = std::is_copy_constructible<T>::value>
         struct Cloner {
            static T *Clone(const T *obj, TDirectory* d = nullptr) {
               T* clone;
               if (d){
                  TDirectory::TContext ctxt(d);
                  clone = new T(*obj);
               } else {
                  clone = new T(*obj);
               }
               return Detacher<T>::Detach(clone);
            }
         };

         template<class T>
         struct Cloner<T, false> {
            static T *Clone(const T *obj, TDirectory* d = nullptr) {
               T* clone;
               if (d){
                  TDirectory::TContext ctxt(d);
                  clone = (T*)obj->Clone();
               } else {
                  clone = (T*)obj->Clone();
               }
               return clone;
            }
         };

         template<class T, bool ISHISTO = std::is_base_of<TH1,T>::value>
         struct DirCreator{
            static std::vector<TDirectory*> Create(unsigned maxSlots) {
               std::string dirName = "__TThreaded_dir_";
               dirName += std::to_string(ROOT::Internal::TThreadedObjectUtils::GetTThreadedObjectIndex()) + "_";
               std::vector<TDirectory*> dirs;
               dirs.reserve(maxSlots);
               for (unsigned i=0; i< maxSlots;++i) {
                  auto dir = gROOT->mkdir((dirName+std::to_string(i)).c_str());
                  dirs.emplace_back(dir);
               }
               return dirs;
            }
         };

         template<class T>
         struct DirCreator<T, true>{
            static std::vector<TDirectory*> Create(unsigned maxSlots) {
               std::vector<TDirectory*> dirs(maxSlots, nullptr);
               return dirs;
            }
         };
      } // End of namespace TThreadedObjectUtils
   } // End of namespace Internal

   namespace TThreadedObjectUtils {

      template<class T>
      using MergeFunctionType = std::function<void(std::shared_ptr<T>, std::vector<std::shared_ptr<T>>&)>;
      /// Merge TObjects
      template<class T>
      void MergeTObjects(std::shared_ptr<T> target, std::vector<std::shared_ptr<T>> &objs)
      {
         if (!target) return;
         TList objTList;
         // Cannot do better than this
         for (auto obj : objs) {
            if (obj && obj != target) objTList.Add(obj.get());
         }
         target->Merge(&objTList);
      }
   } // end of namespace TThreadedObjectUtils

   /**
    * \class ROOT::TThreadedObject
    * \brief A wrapper to make object instances thread private, lazily.
    * \tparam T Class of the object to be made thread private (e.g. TH1F)
    * \ingroup Multicore
    *
    * A wrapper which makes objects thread private. The methods of the underlying
    * object can be invoked via the the arrow operator. The object is created in
    * a specific thread lazily, i.e. upon invocation of one of its methods.
    * The correct object pointer from within a particular thread can be accessed
    * with the overloaded arrow operator or with the Get method.
    * In case an elaborate thread management is in place, e.g. in presence of
    * stream of operations or "processing slots", it is also possible to
    * manually select the correct object pointer explicitly.
    * The default size of the threaded objects is 64. This size can be extended
    * manually via the fgMaxSlots parameter. The size of individual instances
    * is automatically extended if the size of the implicit MT pool is bigger
    * than 64.
    *
    */
   template<class T>
   class TThreadedObject {
   public:
      /// The maximum number of processing slots (distinct threads) which the instances can manage.
      /// Deprecated; please use the `TNumSlots` constructor instead.
      static unsigned fgMaxSlots;

      TThreadedObject(const TThreadedObject&) = delete;

      /// Construct the TThreaded object and the "model" of the thread private
      /// objects. This is the preferred constructor.
      /// \param nslotsTag - set the number of slots (traditionally one per thread) of the
      ///   TThreadedObject: each slot will hold one `T`. If `kIMTPoolSize` is passed, the
      ///   number of slots is defined by ROOT's implicit MT pool size; IMT must be enabled.
      /// \tparam ARGS Arguments of the constructor of T
      template<class ...ARGS>
      TThreadedObject(TNumSlots nslotsTag, ARGS&&... args) : fIsMerged(false), fAutoSlotsWithIMTOff(false)
      {
         auto nslots = nslotsTag.fVal;
         if (nslotsTag == kIMTPoolSize) {
            nslots = ROOT::GetImplicitMTPoolSize();
            if (nslots == 0) {
               throw std::logic_error("Must enable IMT before constructing TThreadedObject(kIMT)!");
            }
         }

         Create(nslots, args...);
      }

      /// Construct the TThreaded object and the "model" of the thread private
      /// objects, assuming IMT usage. Consider using constructor taking `TNumSlots` instead.
      /// \tparam ARGS Arguments of the constructor of T
      template<class ...ARGS>
      TThreadedObject(ARGS&&... args) : fIsMerged(false), fAutoSlotsWithIMTOff(false)
      {
         const auto imtPoolSize = ROOT::GetImplicitMTPoolSize();
         fAutoSlotsWithIMTOff = !imtPoolSize;
         auto nslots = std::max(fgMaxSlots, imtPoolSize);
         Create(nslots, args...);
      }

      /// Access a particular processing slot. This
      /// method is *thread-unsafe*: it cannot be invoked from two different
      /// threads with the same argument.
      std::shared_ptr<T> GetAtSlot(unsigned i)
      {
         if ( i >= fObjPointers.size()) {
            Warning("TThreadedObject::GetAtSlot", "Maximum number of slots reached.");
            if (fAutoSlotsWithIMTOff && ROOT::GetImplicitMTPoolSize() > fObjPointers.size())
               Warning("TThreadedObject::GetAtSlot",
                  "To use TThreadedObject with IMT, turn IMT on before constructing a TThreadedObject.");
            return nullptr;
         }
         auto objPointer = fObjPointers[i];
         if (!objPointer) {
            objPointer.reset(Internal::TThreadedObjectUtils::Cloner<T>::Clone(fModel.get(), fDirectories[i]));
            fObjPointers[i] = objPointer;
         }
         return objPointer;
      }

      /// Set the value of a particular slot.
      void SetAtSlot(unsigned i, std::shared_ptr<T> v)
      {
         fObjPointers[i] = v;
      }

      /// Access a particular slot which corresponds to a single thread.
      /// This is in general faster than the GetAtSlot method but it is
      /// responsibility of the caller to make sure that an object is
      /// initialised for the particular slot.
      std::shared_ptr<T> GetAtSlotUnchecked(unsigned i) const
      {
         return fObjPointers[i];
      }

      /// Access a particular slot which corresponds to a single thread.
      /// This overload is faster than the GetAtSlotUnchecked method but
      /// the caller is responsible to make sure that an object is
      /// initialised for the particular slot and that the returned pointer
      /// will not outlive the TThreadedObject that returned it.
      T* GetAtSlotRaw(unsigned i) const
      {
         return fObjPointers[i].get();
      }

      /// Access the pointer corresponding to the current slot. This method is
      /// not adequate for being called inside tight loops as it implies a
      /// lookup in a mapping between the threadIDs and the slot indices.
      /// A good practice consists in copying the pointer onto the stack and
      /// proceed with the loop as shown in this work item (psudo-code) which
      /// will be sent to different threads:
      /// ~~~{.cpp}
      /// auto workItem = [](){
      ///    auto objPtr = tthreadedObject.Get();
      ///    for (auto i : ROOT::TSeqI(1000)) {
      ///       // tthreadedObject->FastMethod(i); // don't do this! Inefficient!
      ///       objPtr->FastMethod(i);
      ///    }
      /// }
      /// ~~~
      std::shared_ptr<T> Get()
      {
         return GetAtSlot(GetThisSlotNumber());
      }

      /// Access the wrapped object and allow to call its methods.
      T *operator->()
      {
         return Get().get();
      }

      /// Merge all the thread private objects. Can be called once: it does not
      /// create any new object but destroys the present bookkeping collapsing
      /// all objects into the one at slot 0.
      std::shared_ptr<T> Merge(TThreadedObjectUtils::MergeFunctionType<T> mergeFunction = TThreadedObjectUtils::MergeTObjects<T>)
      {
         // We do not return if we already merged.
         if (fIsMerged) {
            Warning("TThreadedObject::Merge", "This object was already merged. Returning the previous result.");
            return fObjPointers[0];
         }
         mergeFunction(fObjPointers[0], fObjPointers);
         fIsMerged = true;
         return fObjPointers[0];
      }

      /// Merge all the thread private objects. Can be called many times. It
      /// does create a new instance of class T to represent the "Sum" object.
      /// This method is not thread safe: correct or acceptable behaviours
      /// depend on the nature of T and of the merging function.
      std::unique_ptr<T> SnapshotMerge(TThreadedObjectUtils::MergeFunctionType<T> mergeFunction = TThreadedObjectUtils::MergeTObjects<T>)
      {
         if (fIsMerged) {
            Warning("TThreadedObject::SnapshotMerge", "This object was already merged. Returning the previous result.");
            return std::unique_ptr<T>(Internal::TThreadedObjectUtils::Cloner<T>::Clone(fObjPointers[0].get()));
         }
         auto targetPtr = Internal::TThreadedObjectUtils::Cloner<T>::Clone(fModel.get());
         std::shared_ptr<T> targetPtrShared(targetPtr, [](T *) {});
         mergeFunction(targetPtrShared, fObjPointers);
         return std::unique_ptr<T>(targetPtr);
      }

   private:
      std::unique_ptr<T> fModel;                         ///< Use to store a "model" of the object
      std::vector<std::shared_ptr<T>> fObjPointers;      ///< A pointer per thread is kept.
      std::vector<TDirectory*> fDirectories;             ///< A TDirectory per thread is kept.
      std::map<std::thread::id, unsigned> fThrIDSlotMap; ///< A mapping between the thread IDs and the slots
      unsigned fCurrMaxSlotIndex = 0;                    ///< The maximum slot index
      ROOT::TSpinMutex fThrIDSlotMutex;                  ///< Mutex to protect the ID-slot map access
      bool fIsMerged : 1;                                ///< Remember if the objects have been merged already

      /// At construction without slot size, IMT was off. It's used to indicate a contract violation,
      /// if later a slot too high is accessed and IMT was turned on in the meantime.
      bool fAutoSlotsWithIMTOff : 1;

      /// Get the slot number for this threadID.
      unsigned GetThisSlotNumber()
      {
         const auto thisThreadID = std::this_thread::get_id();
         unsigned thisIndex;
         {
            std::lock_guard<ROOT::TSpinMutex> lg(fThrIDSlotMutex);
            auto thisSlotNumIt = fThrIDSlotMap.find(thisThreadID);
            if (thisSlotNumIt != fThrIDSlotMap.end()) return thisSlotNumIt->second;
            thisIndex = fCurrMaxSlotIndex++;
            fThrIDSlotMap[thisThreadID] = thisIndex;
         }
         return thisIndex;
      }

      template<class ...ARGS>
      void Create(int nslots, ARGS&&... args) {
         fObjPointers = std::vector<std::shared_ptr<T>>(nslots, nullptr);
         fDirectories = Internal::TThreadedObjectUtils::DirCreator<T>::Create(nslots);

         TDirectory::TContext ctxt(fDirectories[0]);
         fModel.reset(Internal::TThreadedObjectUtils::Detacher<T>::Detach(new T(std::forward<ARGS>(args)...)));
      }
   };

   template<class T> unsigned TThreadedObject<T>::fgMaxSlots = 64;

} // End ROOT namespace

#include <sstream>

////////////////////////////////////////////////////////////////////////////////
/// Print a TThreadedObject at the prompt:

namespace cling {
   template<class T>
   std::string printValue(ROOT::TThreadedObject<T> *val)
   {
      auto model = ((std::unique_ptr<T>*)(val))->get();
      std::ostringstream ret;
      ret << "A wrapper to make object instances thread private, lazily. "
          << "The model which is replicated is " << printValue(model);
      return ret.str();
   }
}

#endif
<|endoftext|>


<commit_before>//
// Aspia Project
// Copyright (C) 2020 Dmitry Chapyshev <dmitry@aspia.ru>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//

#include "desktop/screen_capturer_gdi.h"

#include "base/logging.h"
#include "base/win/scoped_select_object.h"
#include "desktop/win/bitmap_info.h"
#include "desktop/win/screen_capture_utils.h"
#include "desktop/frame_dib.h"
#include "desktop/differ.h"
#include "ipc/shared_memory_factory.h"

#include <dwmapi.h>

namespace desktop {

ScreenCapturerGdi::ScreenCapturerGdi() = default;

ScreenCapturerGdi::~ScreenCapturerGdi()
{
    if (composition_changed_)
        DwmEnableComposition(DWM_EC_ENABLECOMPOSITION);
}

int ScreenCapturerGdi::screenCount()
{
    return ScreenCaptureUtils::screenCount();
}

bool ScreenCapturerGdi::screenList(ScreenList* screens)
{
    return ScreenCaptureUtils::screenList(screens);
}

bool ScreenCapturerGdi::selectScreen(ScreenId screen_id)
{
    if (!ScreenCaptureUtils::isScreenValid(screen_id, &current_device_key_))
        return false;

    // At next screen capture, the resources are recreated.
    desktop_dc_rect_ = Rect();

    current_screen_id_ = screen_id;
    return true;
}

const Frame* ScreenCapturerGdi::captureFrame(Error* error)
{
    DCHECK(error);

    const Frame* frame = captureImage();
    if (!frame)
    {
        *error = Error::TEMPORARY;
        return nullptr;
    }

    *error = Error::SUCCEEDED;
    return frame;
}

void ScreenCapturerGdi::reset()
{
    // Release GDI resources otherwise SetThreadDesktop will fail.
    desktop_dc_.close();
    memory_dc_.reset();
}

const Frame* ScreenCapturerGdi::captureImage()
{
    queue_.moveToNextFrame();

    if (!prepareCaptureResources())
        return nullptr;

    Rect screen_rect = ScreenCaptureUtils::screenRect(current_screen_id_, current_device_key_);
    if (screen_rect.isEmpty())
    {
        LOG(LS_WARNING) << "Failed to get screen rect";
        return nullptr;
    }

    if (!queue_.currentFrame() || queue_.currentFrame()->size() != screen_rect.size())
    {
        DCHECK(desktop_dc_);
        DCHECK(memory_dc_);

        std::unique_ptr<Frame> frame = FrameDib::create(
            screen_rect.size(), PixelFormat::ARGB(), sharedMemoryFactory(), memory_dc_);
        if (!frame)
        {
            LOG(LS_WARNING) << "Failed to create frame buffer";
            return nullptr;
        }

        queue_.replaceCurrentFrame(std::move(frame));
    }

    Frame* current = queue_.currentFrame();
    Frame* previous = queue_.previousFrame();

    {
        base::win::ScopedSelectObject select_object(
            memory_dc_, static_cast<FrameDib*>(current)->bitmap());

        BitBlt(memory_dc_,
               0, 0,
               screen_rect.width(), screen_rect.height(),
               desktop_dc_,
               screen_rect.left(), screen_rect.top(),
               CAPTUREBLT | SRCCOPY);
    }

    current->setTopLeft(screen_rect.topLeft());
    current->setDpi(Point(GetDeviceCaps(desktop_dc_, LOGPIXELSX),
                          GetDeviceCaps(desktop_dc_, LOGPIXELSY)));

    if (!previous || previous->size() != current->size())
    {
        differ_ = std::make_unique<Differ>(screen_rect.size());
        current->updatedRegion()->addRect(Rect::makeSize(screen_rect.size()));
    }
    else
    {
        differ_->calcDirtyRegion(previous->frameData(),
                                 current->frameData(),
                                 current->updatedRegion());
    }

    return current;
}

bool ScreenCapturerGdi::prepareCaptureResources()
{
    Rect desktop_rect = ScreenCaptureUtils::fullScreenRect();

    // If the display bounds have changed then recreate GDI resources.
    if (desktop_rect != desktop_dc_rect_)
    {
        desktop_dc_.close();
        memory_dc_.reset();

        desktop_dc_rect_ = Rect();
    }

    if (!desktop_dc_)
    {
        DCHECK(!memory_dc_);

        BOOL enabled;
        HRESULT hr = DwmIsCompositionEnabled(&enabled);
        if (SUCCEEDED(hr) && enabled)
        {
            // Vote to disable Aero composited desktop effects while capturing.
            // Windows will restore Aero automatically if the process exits.
            // This has no effect under Windows 8 or higher.
            DwmEnableComposition(DWM_EC_DISABLECOMPOSITION);
            composition_changed_ = true;
        }

        // Create GDI device contexts to capture from the desktop into memory.
        desktop_dc_.getDC(nullptr);
        memory_dc_.reset(CreateCompatibleDC(desktop_dc_));
        if (!memory_dc_)
        {
            LOG(LS_WARNING) << "CreateCompatibleDC failed";
            return false;
        }

        desktop_dc_rect_ = desktop_rect;

        // Make sure the frame buffers will be reallocated.
        queue_.reset();
    }

    return true;
}

} // namespace desktop
<commit_msg>Fix top-left coordinates.<commit_after>//
// Aspia Project
// Copyright (C) 2020 Dmitry Chapyshev <dmitry@aspia.ru>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//

#include "desktop/screen_capturer_gdi.h"

#include "base/logging.h"
#include "base/win/scoped_select_object.h"
#include "desktop/win/bitmap_info.h"
#include "desktop/win/screen_capture_utils.h"
#include "desktop/frame_dib.h"
#include "desktop/differ.h"
#include "ipc/shared_memory_factory.h"

#include <dwmapi.h>

namespace desktop {

ScreenCapturerGdi::ScreenCapturerGdi() = default;

ScreenCapturerGdi::~ScreenCapturerGdi()
{
    if (composition_changed_)
        DwmEnableComposition(DWM_EC_ENABLECOMPOSITION);
}

int ScreenCapturerGdi::screenCount()
{
    return ScreenCaptureUtils::screenCount();
}

bool ScreenCapturerGdi::screenList(ScreenList* screens)
{
    return ScreenCaptureUtils::screenList(screens);
}

bool ScreenCapturerGdi::selectScreen(ScreenId screen_id)
{
    if (!ScreenCaptureUtils::isScreenValid(screen_id, &current_device_key_))
        return false;

    // At next screen capture, the resources are recreated.
    desktop_dc_rect_ = Rect();

    current_screen_id_ = screen_id;
    return true;
}

const Frame* ScreenCapturerGdi::captureFrame(Error* error)
{
    DCHECK(error);

    const Frame* frame = captureImage();
    if (!frame)
    {
        *error = Error::TEMPORARY;
        return nullptr;
    }

    *error = Error::SUCCEEDED;
    return frame;
}

void ScreenCapturerGdi::reset()
{
    // Release GDI resources otherwise SetThreadDesktop will fail.
    desktop_dc_.close();
    memory_dc_.reset();
}

const Frame* ScreenCapturerGdi::captureImage()
{
    queue_.moveToNextFrame();

    if (!prepareCaptureResources())
        return nullptr;

    Rect screen_rect = ScreenCaptureUtils::screenRect(current_screen_id_, current_device_key_);
    if (screen_rect.isEmpty())
    {
        LOG(LS_WARNING) << "Failed to get screen rect";
        return nullptr;
    }

    if (!queue_.currentFrame() || queue_.currentFrame()->size() != screen_rect.size())
    {
        DCHECK(desktop_dc_);
        DCHECK(memory_dc_);

        std::unique_ptr<Frame> frame = FrameDib::create(
            screen_rect.size(), PixelFormat::ARGB(), sharedMemoryFactory(), memory_dc_);
        if (!frame)
        {
            LOG(LS_WARNING) << "Failed to create frame buffer";
            return nullptr;
        }

        queue_.replaceCurrentFrame(std::move(frame));
    }

    Frame* current = queue_.currentFrame();
    Frame* previous = queue_.previousFrame();

    {
        base::win::ScopedSelectObject select_object(
            memory_dc_, static_cast<FrameDib*>(current)->bitmap());

        BitBlt(memory_dc_,
               0, 0,
               screen_rect.width(), screen_rect.height(),
               desktop_dc_,
               screen_rect.left(), screen_rect.top(),
               CAPTUREBLT | SRCCOPY);
    }

    current->setTopLeft(screen_rect.topLeft().subtract(desktop_dc_rect_.topLeft()));
    current->setDpi(Point(GetDeviceCaps(desktop_dc_, LOGPIXELSX),
                          GetDeviceCaps(desktop_dc_, LOGPIXELSY)));

    if (!previous || previous->size() != current->size())
    {
        differ_ = std::make_unique<Differ>(screen_rect.size());
        current->updatedRegion()->addRect(Rect::makeSize(screen_rect.size()));
    }
    else
    {
        differ_->calcDirtyRegion(previous->frameData(),
                                 current->frameData(),
                                 current->updatedRegion());
    }

    return current;
}

bool ScreenCapturerGdi::prepareCaptureResources()
{
    Rect desktop_rect = ScreenCaptureUtils::fullScreenRect();

    // If the display bounds have changed then recreate GDI resources.
    if (desktop_rect != desktop_dc_rect_)
    {
        desktop_dc_.close();
        memory_dc_.reset();

        desktop_dc_rect_ = Rect();
    }

    if (!desktop_dc_)
    {
        DCHECK(!memory_dc_);

        BOOL enabled;
        HRESULT hr = DwmIsCompositionEnabled(&enabled);
        if (SUCCEEDED(hr) && enabled)
        {
            // Vote to disable Aero composited desktop effects while capturing.
            // Windows will restore Aero automatically if the process exits.
            // This has no effect under Windows 8 or higher.
            DwmEnableComposition(DWM_EC_DISABLECOMPOSITION);
            composition_changed_ = true;
        }

        // Create GDI device contexts to capture from the desktop into memory.
        desktop_dc_.getDC(nullptr);
        memory_dc_.reset(CreateCompatibleDC(desktop_dc_));
        if (!memory_dc_)
        {
            LOG(LS_WARNING) << "CreateCompatibleDC failed";
            return false;
        }

        desktop_dc_rect_ = desktop_rect;

        // Make sure the frame buffers will be reallocated.
        queue_.reset();
    }

    return true;
}

} // namespace desktop
<|endoftext|>


<commit_before>//---------------------------------------------------------------------------
// Dan Cristian, Rotaru
//---------------------------------------------------------------------------
#include "Earth.h"
#include "TextureLoader.h"

	Earth::Earth() : Sphere(30, 45, 45){
		init();
	}

	Earth::~Earth(){
		Sphere::~Sphere();
	}

	void Earth::init(){

		unsigned int width, height;
		unsigned char * data = loadBMPRaw("earth.bmp", width, height, false);
		// Create one OpenGL texture
		glGenTextures(1, &textureID);

		// "Bind" the newly created texture : all future texture functions will modify this texture
		glBindTexture(GL_TEXTURE_2D, textureID);

		// Give the image to OpenGL
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_BGR_EXT, GL_UNSIGNED_BYTE, data);
		glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

		// Trilinear filtering.
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		delete[] data;
	}

	void Earth::draw(){
		// "Bind" the newly created texture : all future texture functions will modify this texture
		glBindTexture(GL_TEXTURE_2D, textureID);
		gluQuadricTexture(quadric, GL_TRUE);
		Sphere::draw();
	}<commit_msg>v1.03<commit_after>//---------------------------------------------------------------------------
// Dan Cristian, Rotaru
//---------------------------------------------------------------------------//
#include "Earth.h"
#include "TextureLoader.h"

	Earth::Earth() : Sphere(30, 45, 45){
		init();
	}

	Earth::~Earth(){
		Sphere::~Sphere();
	}

	void Earth::init(){

		unsigned int width, height;
		unsigned char * data = loadBMPRaw("earth.bmp", width, height, false);
		// Create one OpenGL texture
		glGenTextures(1, &textureID);

		// "Bind" the newly created texture : all future texture functions will modify this texture
		glBindTexture(GL_TEXTURE_2D, textureID);

		// Give the image to OpenGL
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_BGR_EXT, GL_UNSIGNED_BYTE, data);
		glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

		// Trilinear filtering.
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		delete[] data;
	}

	void Earth::draw(){
		// "Bind" the newly created texture : all future texture functions will modify this texture
		glBindTexture(GL_TEXTURE_2D, textureID);
		gluQuadricTexture(quadric, GL_TRUE);
		Sphere::draw();
	}<|endoftext|>


<commit_before>#include <controller.hh>
#include <ptrace.hh>
#include <utils.hh>

#include <stdlib.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <map>

class DefaultProcessSelector : public IController::IProcessSelector
{
public:
	int selectProcess(int curProcess,
			int nProcesses,
			uint64_t timeUs,
			const IPtrace::PtraceEvent *ev)
	{
		return rand() % nProcesses;
	}
};

class Controller : public IController
{
public:
	Controller()
	{
		m_processes = NULL;

		m_selector = new DefaultProcessSelector();
		m_startTimeStamp = getTimeStamp(0);
	}

	~Controller()
	{
		cleanup();
	}

	void setProcessSelector(IProcessSelector *selector)
	{
		if (m_selector)
			delete m_selector;

		m_selector = selector;
	}

	bool addThread(int (*fn)(void *), void *priv)
	{
		int cur = m_nProcesses;

		m_nProcesses++;
		m_processes = (Process **)realloc(m_processes,
				m_nProcesses * sizeof(Process*));

		// Add the process to the list
		m_processes[cur] = new Process(fn, priv);

		return true;
	}

	bool run()
	{
		bool should_quit = false;

		IPtrace &ptrace = IPtrace::getInstance();
		/* Fork the parent process of all test threads. All
		 * other threads will be threads running in this
		 * context.
		 *
		 * For each round in the test, this is forked again
		 * to retain the original memory state.
		 */
		int pid = ptrace.forkAndAttach();

		if (pid < 0) {
			error("fork failed\n");
			return false;
		} else if (pid == 0) {
			// Child
			for (int i = 0; i < m_nProcesses; i++) {
				Process *cur = m_processes[i];
				int id = ptrace.cloneAndAttach(cur->m_fn, cur->m_priv, cur->m_stack);

				if (id < 0) {
					error("Ptrace clone failed\n");
					exit(125);
				}

				cur->setPid(id);
			}

			int exitCode = 0;

			do {
				pid_t which;
				int status;
				int i;

				which = waitpid(-1, &status, 0);
				if (which < 0) {
					error("cloned thread waitpid failed\n");
					exit(124);
				}
				if (!WIFEXITED(status))
					continue;

				for (i = 0; i < m_nProcesses; i++) {
					if (m_processes[i]->getPid() == which)
						m_processes[i]->exit(WEXITSTATUS(status));
				}

				if (status > exitCode)
					exitCode = status;

				// Done if there are no processes left running
				for (i = 0; i < m_nProcesses; i++) {
					if (!m_processes[i]->hasExited())
						break;
				}
				if (i == m_nProcesses)
					break;
			} while (1);

			exit(exitCode);
		}
		else {
			bool should_quit;

			// Parent
			m_startTimeStamp = getTimeStamp(0);

			do {
				should_quit = !runChild(pid);
			} while (!should_quit);
		}

		return true;
	}

	void setRuns(int nRuns)
	{
	}

	void setTimeLimit(int ms)
	{
	}

private:
	class Process
	{
	public:
		Process(int (*fn)(void *), void *priv) :
			m_fn(fn), m_priv(priv), m_pid(0), m_exitCode(-1)
		{
			/* Plenty of stack */
			size_t stack_sz = 8 * 1024 * 1024;
			m_stackStart = (uint8_t *)xmalloc(stack_sz);

			m_stack = (void *)(m_stackStart + stack_sz - 8);
		}

		~Process()
		{
			free(m_stackStart);
		}

		void exit(int status)
		{
			m_exitCode = status;
		}

		bool hasExited()
		{
			return m_exitCode != -1;
		}

		void setPid(int pid)
		{
			m_pid = pid;
		}

		pid_t getPid()
		{
			return m_pid;
		}

		int (*m_fn)(void *);
		void *m_priv;

		void *m_stack;
	private:
		pid_t m_pid;
		int m_exitCode;

		uint8_t *m_stackStart;
	};

	void cleanup()
	{
		for (int i = 0; i < m_nProcesses; i++)
			delete m_processes[i];

		if (m_selector)
			delete m_selector;

		free(m_processes);
	}

	bool runChild(int pid)
	{
		IPtrace &ptrace = IPtrace::getInstance();

		while (1) {
			const IPtrace::PtraceEvent ev = ptrace.continueExecution(pid);

			switch (ev.type) {
			case ptrace_error:
			case ptrace_crash:
			case ptrace_exit:
				return false;

			case ptrace_breakpoint:
			case ptrace_syscall:
				pid = m_selector->selectProcess(pid, m_nProcesses,
						getTimeStamp(m_startTimeStamp), &ev);
				return true;

			default:
				return false;
			}
		}

		/* Unreachable */
		return false;
	}

	uint64_t getTimeStamp(uint64_t start)
	{
		struct timeval tv;

		gettimeofday(&tv, NULL);

		return (tv.tv_usec + tv.tv_sec * 1000 * 1000) - start;
	}

	int m_nProcesses;
	Process **m_processes;
	IProcessSelector *m_selector;

	uint64_t m_startTimeStamp;

	int m_runLimit;

};

IController &IController::getInstance()
{
	static Controller *instance;

	if (!instance)
		instance = new Controller();

	return *instance;
}
<commit_msg>controller: Replace the Process class with Thread<commit_after>#include <controller.hh>
#include <ptrace.hh>
#include <utils.hh>
#include <thread.hh>

#include <stdlib.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <map>

class DefaultProcessSelector : public IController::IProcessSelector
{
public:
	int selectProcess(int curProcess,
			int nProcesses,
			uint64_t timeUs,
			const IPtrace::PtraceEvent *ev)
	{
		return rand() % nProcesses;
	}
};

class Controller : public IController, IThread::IThreadExitListener
{
public:
	Controller()
	{
		m_processes = NULL;

		m_selector = new DefaultProcessSelector();
		m_startTimeStamp = getTimeStamp(0);
	}

	~Controller()
	{
		cleanup();
	}

	// Thread exit handler from the IThreadExitListener class
	void threadExit(IThread &thread)
	{
		// Do something
		printf("Thread exited\n");
		IThread::releaseThread(thread);
	}

	void setProcessSelector(IProcessSelector *selector)
	{
		if (m_selector)
			delete m_selector;

		m_selector = selector;
	}

	bool addThread(int (*fn)(void *), void *priv)
	{
		int cur = m_nProcesses;

		m_nProcesses++;
		m_processes = (IThread **)realloc(m_processes,
				m_nProcesses * sizeof(IThread *));

		// Add the process to the list
		m_processes[cur] = &IThread::createThread(*this, fn, priv);

		return true;
	}

	bool run()
	{
		bool should_quit = false;

		IPtrace &ptrace = IPtrace::getInstance();
		/* Fork the parent process of all test threads. All
		 * other threads will be threads running in this
		 * context.
		 *
		 * For each round in the test, this is forked again
		 * to retain the original memory state.
		 */
		int pid = ptrace.forkAndAttach();

		if (pid < 0) {
			error("fork failed\n");
			return false;
		} else if (pid == 0) {
			// Hmm... Not sure what to do here
			exit(0);
		}
		else {
			bool should_quit;

			// Parent
			m_startTimeStamp = getTimeStamp(0);

			do {
				should_quit = !runChild(pid);
			} while (!should_quit);
		}

		return true;
	}

	void setRuns(int nRuns)
	{
	}

	void setTimeLimit(int ms)
	{
	}

private:

	void cleanup()
	{
		for (int i = 0; i < m_nProcesses; i++)
			IThread::releaseThread(*m_processes[i]);

		if (m_selector)
			delete m_selector;

		free(m_processes);
	}

	bool runChild(int pid)
	{
		IPtrace &ptrace = IPtrace::getInstance();

		while (1) {
			const IPtrace::PtraceEvent ev = ptrace.continueExecution(pid);

			switch (ev.type) {
			case ptrace_error:
			case ptrace_crash:
			case ptrace_exit:
				return false;

			case ptrace_breakpoint:
			case ptrace_syscall:
				pid = m_selector->selectProcess(pid, m_nProcesses,
						getTimeStamp(m_startTimeStamp), &ev);
				return true;

			default:
				return false;
			}
		}

		/* Unreachable */
		return false;
	}

	uint64_t getTimeStamp(uint64_t start)
	{
		struct timeval tv;

		gettimeofday(&tv, NULL);

		return (tv.tv_usec + tv.tv_sec * 1000 * 1000) - start;
	}

	int m_nProcesses;
	IThread **m_processes;
	IProcessSelector *m_selector;

	uint64_t m_startTimeStamp;

	int m_runLimit;

};

IController &IController::getInstance()
{
	static Controller *instance;

	if (!instance)
		instance = new Controller();

	return *instance;
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org)
 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 *
 */

#include "config.h"
#include "core/rendering/style/FillLayer.h"

namespace WebCore {

struct SameSizeAsFillLayer {
    FillLayer* m_next;

    RefPtr<StyleImage> m_image;

    Length m_xPosition;
    Length m_yPosition;

    LengthSize m_sizeLength;

    unsigned m_bitfields: 32;
    unsigned m_bitfields2: 1;
};

COMPILE_ASSERT(sizeof(FillLayer) == sizeof(SameSizeAsFillLayer), FillLayer_should_stay_small);

FillLayer::FillLayer(EFillLayerType type, bool useInitialValues)
    : m_next(0)
    , m_image(FillLayer::initialFillImage(type))
    , m_xPosition(FillLayer::initialFillXPosition(type))
    , m_yPosition(FillLayer::initialFillYPosition(type))
    , m_sizeLength(FillLayer::initialFillSizeLength(type))
    , m_attachment(FillLayer::initialFillAttachment(type))
    , m_clip(FillLayer::initialFillClip(type))
    , m_origin(FillLayer::initialFillOrigin(type))
    , m_repeatX(FillLayer::initialFillRepeatX(type))
    , m_repeatY(FillLayer::initialFillRepeatY(type))
    , m_composite(FillLayer::initialFillComposite(type))
    , m_sizeType(useInitialValues ? FillLayer::initialFillSizeType(type) : SizeNone)
    , m_blendMode(FillLayer::initialFillBlendMode(type))
    , m_maskSourceType(FillLayer::initialFillMaskSourceType(type))
    , m_backgroundXOrigin(LeftEdge)
    , m_backgroundYOrigin(TopEdge)
    , m_imageSet(useInitialValues)
    , m_attachmentSet(useInitialValues)
    , m_clipSet(useInitialValues)
    , m_originSet(useInitialValues)
    , m_repeatXSet(useInitialValues)
    , m_repeatYSet(useInitialValues)
    , m_xPosSet(useInitialValues)
    , m_yPosSet(useInitialValues)
    , m_backgroundXOriginSet(false)
    , m_backgroundYOriginSet(false)
    , m_compositeSet(useInitialValues || type == MaskFillLayer)
    , m_blendModeSet(useInitialValues)
    , m_maskSourceTypeSet(useInitialValues)
    , m_type(type)
{
}

FillLayer::FillLayer(const FillLayer& o)
    : m_next(o.m_next ? new FillLayer(*o.m_next) : 0)
    , m_image(o.m_image)
    , m_xPosition(o.m_xPosition)
    , m_yPosition(o.m_yPosition)
    , m_sizeLength(o.m_sizeLength)
    , m_attachment(o.m_attachment)
    , m_clip(o.m_clip)
    , m_origin(o.m_origin)
    , m_repeatX(o.m_repeatX)
    , m_repeatY(o.m_repeatY)
    , m_composite(o.m_composite)
    , m_sizeType(o.m_sizeType)
    , m_blendMode(o.m_blendMode)
    , m_maskSourceType(o.m_maskSourceType)
    , m_backgroundXOrigin(o.m_backgroundXOrigin)
    , m_backgroundYOrigin(o.m_backgroundYOrigin)
    , m_imageSet(o.m_imageSet)
    , m_attachmentSet(o.m_attachmentSet)
    , m_clipSet(o.m_clipSet)
    , m_originSet(o.m_originSet)
    , m_repeatXSet(o.m_repeatXSet)
    , m_repeatYSet(o.m_repeatYSet)
    , m_xPosSet(o.m_xPosSet)
    , m_yPosSet(o.m_yPosSet)
    , m_backgroundXOriginSet(o.m_backgroundXOriginSet)
    , m_backgroundYOriginSet(o.m_backgroundYOriginSet)
    , m_compositeSet(o.m_compositeSet)
    , m_blendModeSet(o.m_blendModeSet)
    , m_maskSourceTypeSet(o.m_maskSourceTypeSet)
    , m_type(o.m_type)
{
}

FillLayer::~FillLayer()
{
    delete m_next;
}

FillLayer& FillLayer::operator=(const FillLayer& o)
{
    if (m_next != o.m_next) {
        delete m_next;
        m_next = o.m_next ? new FillLayer(*o.m_next) : 0;
    }

    m_image = o.m_image;
    m_xPosition = o.m_xPosition;
    m_yPosition = o.m_yPosition;
    m_backgroundXOrigin = o.m_backgroundXOrigin;
    m_backgroundYOrigin = o.m_backgroundYOrigin;
    m_backgroundXOriginSet = o.m_backgroundXOriginSet;
    m_backgroundYOriginSet = o.m_backgroundYOriginSet;
    m_sizeLength = o.m_sizeLength;
    m_attachment = o.m_attachment;
    m_clip = o.m_clip;
    m_composite = o.m_composite;
    m_blendMode = o.m_blendMode;
    m_origin = o.m_origin;
    m_repeatX = o.m_repeatX;
    m_repeatY = o.m_repeatY;
    m_sizeType = o.m_sizeType;
    m_maskSourceType = o.m_maskSourceType;

    m_imageSet = o.m_imageSet;
    m_attachmentSet = o.m_attachmentSet;
    m_clipSet = o.m_clipSet;
    m_compositeSet = o.m_compositeSet;
    m_blendModeSet = o.m_blendModeSet;
    m_originSet = o.m_originSet;
    m_repeatXSet = o.m_repeatXSet;
    m_repeatYSet = o.m_repeatYSet;
    m_xPosSet = o.m_xPosSet;
    m_yPosSet = o.m_yPosSet;
    m_maskSourceTypeSet = o.m_maskSourceTypeSet;

    m_type = o.m_type;

    return *this;
}

bool FillLayer::operator==(const FillLayer& o) const
{
    // We do not check the "isSet" booleans for each property, since those are only used during initial construction
    // to propagate patterns into layers.  All layer comparisons happen after values have all been filled in anyway.
    return StyleImage::imagesEquivalent(m_image.get(), o.m_image.get()) && m_xPosition == o.m_xPosition && m_yPosition == o.m_yPosition
            && m_backgroundXOrigin == o.m_backgroundXOrigin && m_backgroundYOrigin == o.m_backgroundYOrigin
            && m_attachment == o.m_attachment && m_clip == o.m_clip && m_composite == o.m_composite
            && m_blendMode == o.m_blendMode && m_origin == o.m_origin && m_repeatX == o.m_repeatX
            && m_repeatY == o.m_repeatY && m_sizeType == o.m_sizeType && m_maskSourceType == o.m_maskSourceType
            && m_sizeLength == o.m_sizeLength && m_type == o.m_type
            && ((m_next && o.m_next) ? *m_next == *o.m_next : m_next == o.m_next);
}

void FillLayer::fillUnsetProperties()
{
    FillLayer* curr;
    for (curr = this; curr && curr->isXPositionSet(); curr = curr->next()) { }
    if (curr && curr != this) {
        // We need to fill in the remaining values with the pattern specified.
        for (FillLayer* pattern = this; curr; curr = curr->next()) {
            curr->m_xPosition = pattern->m_xPosition;
            if (pattern->isBackgroundXOriginSet())
                curr->m_backgroundXOrigin = pattern->m_backgroundXOrigin;
            if (pattern->isBackgroundYOriginSet())
                curr->m_backgroundYOrigin = pattern->m_backgroundYOrigin;
            pattern = pattern->next();
            if (pattern == curr || !pattern)
                pattern = this;
        }
    }

    for (curr = this; curr && curr->isYPositionSet(); curr = curr->next()) { }
    if (curr && curr != this) {
        // We need to fill in the remaining values with the pattern specified.
        for (FillLayer* pattern = this; curr; curr = curr->next()) {
            curr->m_yPosition = pattern->m_yPosition;
            if (pattern->isBackgroundXOriginSet())
                curr->m_backgroundXOrigin = pattern->m_backgroundXOrigin;
            if (pattern->isBackgroundYOriginSet())
                curr->m_backgroundYOrigin = pattern->m_backgroundYOrigin;
            pattern = pattern->next();
            if (pattern == curr || !pattern)
                pattern = this;
        }
    }

    for (curr = this; curr && curr->isAttachmentSet(); curr = curr->next()) { }
    if (curr && curr != this) {
        // We need to fill in the remaining values with the pattern specified.
        for (FillLayer* pattern = this; curr; curr = curr->next()) {
            curr->m_attachment = pattern->m_attachment;
            pattern = pattern->next();
            if (pattern == curr || !pattern)
                pattern = this;
        }
    }

    for (curr = this; curr && curr->isClipSet(); curr = curr->next()) { }
    if (curr && curr != this) {
        // We need to fill in the remaining values with the pattern specified.
        for (FillLayer* pattern = this; curr; curr = curr->next()) {
            curr->m_clip = pattern->m_clip;
            pattern = pattern->next();
            if (pattern == curr || !pattern)
                pattern = this;
        }
    }

    for (curr = this; curr && curr->isCompositeSet(); curr = curr->next()) { }
    if (curr && curr != this) {
        // We need to fill in the remaining values with the pattern specified.
        for (FillLayer* pattern = this; curr; curr = curr->next()) {
            curr->m_composite = pattern->m_composite;
            pattern = pattern->next();
            if (pattern == curr || !pattern)
                pattern = this;
        }
    }

    for (curr = this; curr && curr->isBlendModeSet(); curr = curr->next()) { }
    if (curr && curr != this) {
        // We need to fill in the remaining values with the pattern specified.
        for (FillLayer* pattern = this; curr; curr = curr->next()) {
            curr->m_blendMode = pattern->m_blendMode;
            pattern = pattern->next();
            if (pattern == curr || !pattern)
                pattern = this;
        }
    }

    for (curr = this; curr && curr->isOriginSet(); curr = curr->next()) { }
    if (curr && curr != this) {
        // We need to fill in the remaining values with the pattern specified.
        for (FillLayer* pattern = this; curr; curr = curr->next()) {
            curr->m_origin = pattern->m_origin;
            pattern = pattern->next();
            if (pattern == curr || !pattern)
                pattern = this;
        }
    }

    for (curr = this; curr && curr->isRepeatXSet(); curr = curr->next()) { }
    if (curr && curr != this) {
        // We need to fill in the remaining values with the pattern specified.
        for (FillLayer* pattern = this; curr; curr = curr->next()) {
            curr->m_repeatX = pattern->m_repeatX;
            pattern = pattern->next();
            if (pattern == curr || !pattern)
                pattern = this;
        }
    }

    for (curr = this; curr && curr->isRepeatYSet(); curr = curr->next()) { }
    if (curr && curr != this) {
        // We need to fill in the remaining values with the pattern specified.
        for (FillLayer* pattern = this; curr; curr = curr->next()) {
            curr->m_repeatY = pattern->m_repeatY;
            pattern = pattern->next();
            if (pattern == curr || !pattern)
                pattern = this;
        }
    }

    for (curr = this; curr && curr->isSizeSet(); curr = curr->next()) { }
    if (curr && curr != this) {
        // We need to fill in the remaining values with the pattern specified.
        for (FillLayer* pattern = this; curr; curr = curr->next()) {
            curr->m_sizeType = pattern->m_sizeType;
            curr->m_sizeLength = pattern->m_sizeLength;
            pattern = pattern->next();
            if (pattern == curr || !pattern)
                pattern = this;
        }
    }
}

void FillLayer::cullEmptyLayers()
{
    FillLayer* next;
    for (FillLayer* p = this; p; p = next) {
        next = p->m_next;
        if (next && !next->isImageSet()) {
            delete next;
            p->m_next = 0;
            break;
        }
    }
}

static EFillBox clipMax(EFillBox clipA, EFillBox clipB)
{
    if (clipA == BorderFillBox || clipB == BorderFillBox)
        return BorderFillBox;
    if (clipA == PaddingFillBox || clipB == PaddingFillBox)
        return PaddingFillBox;
    if (clipA == ContentFillBox || clipB == ContentFillBox)
        return ContentFillBox;
    return TextFillBox;
}

void FillLayer::computeClipMax() const
{
    if (m_next) {
        m_next->computeClipMax();
        m_clipMax = clipMax(clip(), m_next->clip());
    } else
        m_clipMax = m_clip;
}

bool FillLayer::clipOccludesNextLayers(bool firstLayer) const
{
    if (firstLayer)
        computeClipMax();
    return m_clip == m_clipMax;
}

bool FillLayer::containsImage(StyleImage* s) const
{
    if (!s)
        return false;
    if (m_image && *s == *m_image)
        return true;
    if (m_next)
        return m_next->containsImage(s);
    return false;
}

bool FillLayer::imagesAreLoaded() const
{
    const FillLayer* curr;
    for (curr = this; curr; curr = curr->next()) {
        if (curr->m_image && !curr->m_image->isLoaded())
            return false;
    }

    return true;
}

bool FillLayer::hasOpaqueImage(const RenderObject* renderer) const
{
    if (!m_image)
        return false;

    if (m_composite == CompositeClear || m_composite == CompositeCopy)
        return true;

    if (m_blendMode != blink::WebBlendModeNormal)
        return false;

    if (m_composite == CompositeSourceOver)
        return m_image->knownToBeOpaque(renderer);

    return false;
}

bool FillLayer::hasRepeatXY() const
{
    return m_repeatX == RepeatFill && m_repeatY == RepeatFill;
}

} // namespace WebCore
<commit_msg>Align SameSizeAsFillLayer with FillLayer<commit_after>/*
 * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org)
 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 *
 */

#include "config.h"
#include "core/rendering/style/FillLayer.h"

namespace WebCore {

struct SameSizeAsFillLayer {
    FillLayer* m_next;

    RefPtr<StyleImage> m_image;

    Length m_xPosition;
    Length m_yPosition;

    LengthSize m_sizeLength;

    unsigned m_bitfields1;
    unsigned m_bitfields2;
};

COMPILE_ASSERT(sizeof(FillLayer) == sizeof(SameSizeAsFillLayer), FillLayer_should_stay_small);

FillLayer::FillLayer(EFillLayerType type, bool useInitialValues)
    : m_next(0)
    , m_image(FillLayer::initialFillImage(type))
    , m_xPosition(FillLayer::initialFillXPosition(type))
    , m_yPosition(FillLayer::initialFillYPosition(type))
    , m_sizeLength(FillLayer::initialFillSizeLength(type))
    , m_attachment(FillLayer::initialFillAttachment(type))
    , m_clip(FillLayer::initialFillClip(type))
    , m_origin(FillLayer::initialFillOrigin(type))
    , m_repeatX(FillLayer::initialFillRepeatX(type))
    , m_repeatY(FillLayer::initialFillRepeatY(type))
    , m_composite(FillLayer::initialFillComposite(type))
    , m_sizeType(useInitialValues ? FillLayer::initialFillSizeType(type) : SizeNone)
    , m_blendMode(FillLayer::initialFillBlendMode(type))
    , m_maskSourceType(FillLayer::initialFillMaskSourceType(type))
    , m_backgroundXOrigin(LeftEdge)
    , m_backgroundYOrigin(TopEdge)
    , m_imageSet(useInitialValues)
    , m_attachmentSet(useInitialValues)
    , m_clipSet(useInitialValues)
    , m_originSet(useInitialValues)
    , m_repeatXSet(useInitialValues)
    , m_repeatYSet(useInitialValues)
    , m_xPosSet(useInitialValues)
    , m_yPosSet(useInitialValues)
    , m_backgroundXOriginSet(false)
    , m_backgroundYOriginSet(false)
    , m_compositeSet(useInitialValues || type == MaskFillLayer)
    , m_blendModeSet(useInitialValues)
    , m_maskSourceTypeSet(useInitialValues)
    , m_type(type)
{
}

FillLayer::FillLayer(const FillLayer& o)
    : m_next(o.m_next ? new FillLayer(*o.m_next) : 0)
    , m_image(o.m_image)
    , m_xPosition(o.m_xPosition)
    , m_yPosition(o.m_yPosition)
    , m_sizeLength(o.m_sizeLength)
    , m_attachment(o.m_attachment)
    , m_clip(o.m_clip)
    , m_origin(o.m_origin)
    , m_repeatX(o.m_repeatX)
    , m_repeatY(o.m_repeatY)
    , m_composite(o.m_composite)
    , m_sizeType(o.m_sizeType)
    , m_blendMode(o.m_blendMode)
    , m_maskSourceType(o.m_maskSourceType)
    , m_backgroundXOrigin(o.m_backgroundXOrigin)
    , m_backgroundYOrigin(o.m_backgroundYOrigin)
    , m_imageSet(o.m_imageSet)
    , m_attachmentSet(o.m_attachmentSet)
    , m_clipSet(o.m_clipSet)
    , m_originSet(o.m_originSet)
    , m_repeatXSet(o.m_repeatXSet)
    , m_repeatYSet(o.m_repeatYSet)
    , m_xPosSet(o.m_xPosSet)
    , m_yPosSet(o.m_yPosSet)
    , m_backgroundXOriginSet(o.m_backgroundXOriginSet)
    , m_backgroundYOriginSet(o.m_backgroundYOriginSet)
    , m_compositeSet(o.m_compositeSet)
    , m_blendModeSet(o.m_blendModeSet)
    , m_maskSourceTypeSet(o.m_maskSourceTypeSet)
    , m_type(o.m_type)
{
}

FillLayer::~FillLayer()
{
    delete m_next;
}

FillLayer& FillLayer::operator=(const FillLayer& o)
{
    if (m_next != o.m_next) {
        delete m_next;
        m_next = o.m_next ? new FillLayer(*o.m_next) : 0;
    }

    m_image = o.m_image;
    m_xPosition = o.m_xPosition;
    m_yPosition = o.m_yPosition;
    m_backgroundXOrigin = o.m_backgroundXOrigin;
    m_backgroundYOrigin = o.m_backgroundYOrigin;
    m_backgroundXOriginSet = o.m_backgroundXOriginSet;
    m_backgroundYOriginSet = o.m_backgroundYOriginSet;
    m_sizeLength = o.m_sizeLength;
    m_attachment = o.m_attachment;
    m_clip = o.m_clip;
    m_composite = o.m_composite;
    m_blendMode = o.m_blendMode;
    m_origin = o.m_origin;
    m_repeatX = o.m_repeatX;
    m_repeatY = o.m_repeatY;
    m_sizeType = o.m_sizeType;
    m_maskSourceType = o.m_maskSourceType;

    m_imageSet = o.m_imageSet;
    m_attachmentSet = o.m_attachmentSet;
    m_clipSet = o.m_clipSet;
    m_compositeSet = o.m_compositeSet;
    m_blendModeSet = o.m_blendModeSet;
    m_originSet = o.m_originSet;
    m_repeatXSet = o.m_repeatXSet;
    m_repeatYSet = o.m_repeatYSet;
    m_xPosSet = o.m_xPosSet;
    m_yPosSet = o.m_yPosSet;
    m_maskSourceTypeSet = o.m_maskSourceTypeSet;

    m_type = o.m_type;

    return *this;
}

bool FillLayer::operator==(const FillLayer& o) const
{
    // We do not check the "isSet" booleans for each property, since those are only used during initial construction
    // to propagate patterns into layers.  All layer comparisons happen after values have all been filled in anyway.
    return StyleImage::imagesEquivalent(m_image.get(), o.m_image.get()) && m_xPosition == o.m_xPosition && m_yPosition == o.m_yPosition
            && m_backgroundXOrigin == o.m_backgroundXOrigin && m_backgroundYOrigin == o.m_backgroundYOrigin
            && m_attachment == o.m_attachment && m_clip == o.m_clip && m_composite == o.m_composite
            && m_blendMode == o.m_blendMode && m_origin == o.m_origin && m_repeatX == o.m_repeatX
            && m_repeatY == o.m_repeatY && m_sizeType == o.m_sizeType && m_maskSourceType == o.m_maskSourceType
            && m_sizeLength == o.m_sizeLength && m_type == o.m_type
            && ((m_next && o.m_next) ? *m_next == *o.m_next : m_next == o.m_next);
}

void FillLayer::fillUnsetProperties()
{
    FillLayer* curr;
    for (curr = this; curr && curr->isXPositionSet(); curr = curr->next()) { }
    if (curr && curr != this) {
        // We need to fill in the remaining values with the pattern specified.
        for (FillLayer* pattern = this; curr; curr = curr->next()) {
            curr->m_xPosition = pattern->m_xPosition;
            if (pattern->isBackgroundXOriginSet())
                curr->m_backgroundXOrigin = pattern->m_backgroundXOrigin;
            if (pattern->isBackgroundYOriginSet())
                curr->m_backgroundYOrigin = pattern->m_backgroundYOrigin;
            pattern = pattern->next();
            if (pattern == curr || !pattern)
                pattern = this;
        }
    }

    for (curr = this; curr && curr->isYPositionSet(); curr = curr->next()) { }
    if (curr && curr != this) {
        // We need to fill in the remaining values with the pattern specified.
        for (FillLayer* pattern = this; curr; curr = curr->next()) {
            curr->m_yPosition = pattern->m_yPosition;
            if (pattern->isBackgroundXOriginSet())
                curr->m_backgroundXOrigin = pattern->m_backgroundXOrigin;
            if (pattern->isBackgroundYOriginSet())
                curr->m_backgroundYOrigin = pattern->m_backgroundYOrigin;
            pattern = pattern->next();
            if (pattern == curr || !pattern)
                pattern = this;
        }
    }

    for (curr = this; curr && curr->isAttachmentSet(); curr = curr->next()) { }
    if (curr && curr != this) {
        // We need to fill in the remaining values with the pattern specified.
        for (FillLayer* pattern = this; curr; curr = curr->next()) {
            curr->m_attachment = pattern->m_attachment;
            pattern = pattern->next();
            if (pattern == curr || !pattern)
                pattern = this;
        }
    }

    for (curr = this; curr && curr->isClipSet(); curr = curr->next()) { }
    if (curr && curr != this) {
        // We need to fill in the remaining values with the pattern specified.
        for (FillLayer* pattern = this; curr; curr = curr->next()) {
            curr->m_clip = pattern->m_clip;
            pattern = pattern->next();
            if (pattern == curr || !pattern)
                pattern = this;
        }
    }

    for (curr = this; curr && curr->isCompositeSet(); curr = curr->next()) { }
    if (curr && curr != this) {
        // We need to fill in the remaining values with the pattern specified.
        for (FillLayer* pattern = this; curr; curr = curr->next()) {
            curr->m_composite = pattern->m_composite;
            pattern = pattern->next();
            if (pattern == curr || !pattern)
                pattern = this;
        }
    }

    for (curr = this; curr && curr->isBlendModeSet(); curr = curr->next()) { }
    if (curr && curr != this) {
        // We need to fill in the remaining values with the pattern specified.
        for (FillLayer* pattern = this; curr; curr = curr->next()) {
            curr->m_blendMode = pattern->m_blendMode;
            pattern = pattern->next();
            if (pattern == curr || !pattern)
                pattern = this;
        }
    }

    for (curr = this; curr && curr->isOriginSet(); curr = curr->next()) { }
    if (curr && curr != this) {
        // We need to fill in the remaining values with the pattern specified.
        for (FillLayer* pattern = this; curr; curr = curr->next()) {
            curr->m_origin = pattern->m_origin;
            pattern = pattern->next();
            if (pattern == curr || !pattern)
                pattern = this;
        }
    }

    for (curr = this; curr && curr->isRepeatXSet(); curr = curr->next()) { }
    if (curr && curr != this) {
        // We need to fill in the remaining values with the pattern specified.
        for (FillLayer* pattern = this; curr; curr = curr->next()) {
            curr->m_repeatX = pattern->m_repeatX;
            pattern = pattern->next();
            if (pattern == curr || !pattern)
                pattern = this;
        }
    }

    for (curr = this; curr && curr->isRepeatYSet(); curr = curr->next()) { }
    if (curr && curr != this) {
        // We need to fill in the remaining values with the pattern specified.
        for (FillLayer* pattern = this; curr; curr = curr->next()) {
            curr->m_repeatY = pattern->m_repeatY;
            pattern = pattern->next();
            if (pattern == curr || !pattern)
                pattern = this;
        }
    }

    for (curr = this; curr && curr->isSizeSet(); curr = curr->next()) { }
    if (curr && curr != this) {
        // We need to fill in the remaining values with the pattern specified.
        for (FillLayer* pattern = this; curr; curr = curr->next()) {
            curr->m_sizeType = pattern->m_sizeType;
            curr->m_sizeLength = pattern->m_sizeLength;
            pattern = pattern->next();
            if (pattern == curr || !pattern)
                pattern = this;
        }
    }
}

void FillLayer::cullEmptyLayers()
{
    FillLayer* next;
    for (FillLayer* p = this; p; p = next) {
        next = p->m_next;
        if (next && !next->isImageSet()) {
            delete next;
            p->m_next = 0;
            break;
        }
    }
}

static EFillBox clipMax(EFillBox clipA, EFillBox clipB)
{
    if (clipA == BorderFillBox || clipB == BorderFillBox)
        return BorderFillBox;
    if (clipA == PaddingFillBox || clipB == PaddingFillBox)
        return PaddingFillBox;
    if (clipA == ContentFillBox || clipB == ContentFillBox)
        return ContentFillBox;
    return TextFillBox;
}

void FillLayer::computeClipMax() const
{
    if (m_next) {
        m_next->computeClipMax();
        m_clipMax = clipMax(clip(), m_next->clip());
    } else
        m_clipMax = m_clip;
}

bool FillLayer::clipOccludesNextLayers(bool firstLayer) const
{
    if (firstLayer)
        computeClipMax();
    return m_clip == m_clipMax;
}

bool FillLayer::containsImage(StyleImage* s) const
{
    if (!s)
        return false;
    if (m_image && *s == *m_image)
        return true;
    if (m_next)
        return m_next->containsImage(s);
    return false;
}

bool FillLayer::imagesAreLoaded() const
{
    const FillLayer* curr;
    for (curr = this; curr; curr = curr->next()) {
        if (curr->m_image && !curr->m_image->isLoaded())
            return false;
    }

    return true;
}

bool FillLayer::hasOpaqueImage(const RenderObject* renderer) const
{
    if (!m_image)
        return false;

    if (m_composite == CompositeClear || m_composite == CompositeCopy)
        return true;

    if (m_blendMode != blink::WebBlendModeNormal)
        return false;

    if (m_composite == CompositeSourceOver)
        return m_image->knownToBeOpaque(renderer);

    return false;
}

bool FillLayer::hasRepeatXY() const
{
    return m_repeatX == RepeatFill && m_repeatY == RepeatFill;
}

} // namespace WebCore
<|endoftext|>


<commit_before>#include "astar.hpp"
#include "backtrace.hpp"

bool HeapCompare_f::operator() (Node* x, Node* y) {
    return x->f > y->f;
}

AStarFinder AStarFinder_constructor(bool allowDiagonal, bool dontCrossCorners, int weight) {
    AStarFinder astar = { allowDiagonal, dontCrossCorners, weight, 0 };

    if (!astar.allowDiagonal) {
        astar.diagonalMovement = DM_NEVER;
    } else {
        if (astar.dontCrossCorners) {
            astar.diagonalMovement = DM_ONLYWHENNOOBSTACLES;
        } else {
            astar.diagonalMovement = DM_IFATMOSTONEOBSTACLE;
        }
    }

    return astar;
}

float AStarFinder_heuristic(AStarFinder astar, int dx, int dy) {
    float dx_ = (float) dx;
    float dy_ = (float) dy;
    if (astar.diagonalMovement == DM_NEVER) {
        return dx_ + dy_; // 4 ways => manhattan
    } else {
        return (dx < dy) ? F * dx_ + dx_ : F * dy_ + dx_; // 8 ways => octile
    }
}

vector<vector<int>> AStarFinder_findPath(AStarFinder astar, int startX, int startY, int endX, int endY, Grid* grid) {
    Node* startNode     = Grid_getNodeAt(grid, startX, startY);
    Node* endNode       = Grid_getNodeAt(grid, endX, endY);
    DiagonalMovement dm = astar.diagonalMovement;
    int weight          = astar.weight;

    Node* node;
    Node* neighbor;
    vector<Node*> neighbors(MAX_NEIGHBORS, nullptr);
    int i, l, x, y;
    float ng;

    priority_queue<Node*, vector<Node*>, HeapCompare_f> openList;

    printf("Astar from %d %d to %d %d \n", startX, startY, endX, endY);

    startNode->g = 0;
    startNode->f = 0;
    startNode->opened = true;

    openList.push(startNode);

    while (!openList.empty()) {
        node = openList.top();
        openList.pop();

        node->closed = true;

        if (node->x == endNode->x && node->y == endNode->y) {
            return backtrace(endNode);
        }

        neighbors = Grid_getNeighbors(grid, *node, dm);

        for (i = 0, l = MAX_NEIGHBORS; i < l; ++i) {
            // We don't know how neighbors there is
            if (neighbors[i] == nullptr) {
                continue;
            }

            neighbor = neighbors[i];

            if (neighbor->closed) {
                continue;
            }

            x = neighbor->x;
            y = neighbor->y;

            ng = node->g + ((x - node->x == 0 || y - node->y == 0) ? 1 : SQRT2);

            if (!neighbor->opened || ng < neighbor->g) {
                neighbor->g = ng;
                neighbor->h = (neighbor->h) ? neighbor->h : weight * AStarFinder_heuristic(astar, abs(x - endX), abs(y - endY));
                neighbor->f = neighbor->g + neighbor->h;

                neighbor->parent = node;

                if (!neighbor->opened) {
                    openList.push(neighbor);
                    neighbor->opened = true;
                } else {
                    // Updated an item, sort again the priority queue using make_heap
                    make_heap(const_cast<Node**>(&openList.top()),
                              const_cast<Node**>(&openList.top()) + openList.size(),
                              HeapCompare_f());
                }
            }
        }
    }

    vector<vector<int>> notFound(0);
    return notFound;
}

<commit_msg>Remove printf<commit_after>#include "astar.hpp"
#include "backtrace.hpp"

bool HeapCompare_f::operator() (Node* x, Node* y) {
    return x->f > y->f;
}

AStarFinder AStarFinder_constructor(bool allowDiagonal, bool dontCrossCorners, int weight) {
    AStarFinder astar = { allowDiagonal, dontCrossCorners, weight, 0 };

    if (!astar.allowDiagonal) {
        astar.diagonalMovement = DM_NEVER;
    } else {
        if (astar.dontCrossCorners) {
            astar.diagonalMovement = DM_ONLYWHENNOOBSTACLES;
        } else {
            astar.diagonalMovement = DM_IFATMOSTONEOBSTACLE;
        }
    }

    return astar;
}

float AStarFinder_heuristic(AStarFinder astar, int dx, int dy) {
    float dx_ = (float) dx;
    float dy_ = (float) dy;
    if (astar.diagonalMovement == DM_NEVER) {
        return dx_ + dy_; // 4 ways => manhattan
    } else {
        return (dx < dy) ? F * dx_ + dx_ : F * dy_ + dx_; // 8 ways => octile
    }
}

vector<vector<int>> AStarFinder_findPath(AStarFinder astar, int startX, int startY, int endX, int endY, Grid* grid) {
    Node* startNode     = Grid_getNodeAt(grid, startX, startY);
    Node* endNode       = Grid_getNodeAt(grid, endX, endY);
    DiagonalMovement dm = astar.diagonalMovement;
    int weight          = astar.weight;

    Node* node;
    Node* neighbor;
    vector<Node*> neighbors(MAX_NEIGHBORS, nullptr);
    int i, l, x, y;
    float ng;

    priority_queue<Node*, vector<Node*>, HeapCompare_f> openList;

    startNode->g = 0;
    startNode->f = 0;
    startNode->opened = true;

    openList.push(startNode);

    while (!openList.empty()) {
        node = openList.top();
        openList.pop();

        node->closed = true;

        if (node->x == endNode->x && node->y == endNode->y) {
            return backtrace(endNode);
        }

        neighbors = Grid_getNeighbors(grid, *node, dm);

        for (i = 0, l = MAX_NEIGHBORS; i < l; ++i) {
            // We don't know how neighbors there is
            if (neighbors[i] == nullptr) {
                continue;
            }

            neighbor = neighbors[i];

            if (neighbor->closed) {
                continue;
            }

            x = neighbor->x;
            y = neighbor->y;

            ng = node->g + ((x - node->x == 0 || y - node->y == 0) ? 1 : SQRT2);

            if (!neighbor->opened || ng < neighbor->g) {
                neighbor->g = ng;
                neighbor->h = (neighbor->h) ? neighbor->h : weight * AStarFinder_heuristic(astar, abs(x - endX), abs(y - endY));
                neighbor->f = neighbor->g + neighbor->h;

                neighbor->parent = node;

                if (!neighbor->opened) {
                    openList.push(neighbor);
                    neighbor->opened = true;
                } else {
                    // Updated an item, sort again the priority queue using make_heap
                    make_heap(const_cast<Node**>(&openList.top()),
                              const_cast<Node**>(&openList.top()) + openList.size(),
                              HeapCompare_f());
                }
            }
        }
    }

    vector<vector<int>> notFound(0);
    return notFound;
}

<|endoftext|>


<commit_before>#include <cctype>

#include <fuzzywuzzy.hpp>

#include "../string.hpp"
#include "algorithm.hpp"
#include "common.hpp"
#include "item.hpp"

namespace bookwyrm::core {

    size_t item::items_idx = 0;

    static int get_integral(const py::dict &dict, const char *key)
    {
        if (!dict.contains(key) || dict[key].is_none())
            return empty;
        return py::int_(dict[key]);
    }

    static string get_string(const py::dict &dict, const char *key)
    {
        if (!dict.contains(key) || dict[key].is_none())
            return "";
        return trim(py::str(dict[key]));
    }

    static vector<string> get_vector_string(const py::dict &dict, const char *key)
    {
        if (!dict.contains(key) || dict[key].is_none())
            return {{}};

        vector<string> strings;
        for (auto &handle : py::list(dict[key]))
            strings.push_back(trim(py::str(handle)));
        return strings;
    }

    exacts_t::exacts_t(const py::dict &dict)
        : ymod(year_mod::unused), year(get_integral(dict, "year")), volume(get_integral(dict, "volume")),
          number(get_integral(dict, "number")), pages(get_integral(dict, "pages")), size(get_integral(dict, "size")),
          extension(get_string(dict, "extension"))
    {
    }

    nonexacts_t::nonexacts_t(const py::dict &dict)
        : authors(get_vector_string(dict, "authors")), title(get_string(dict, "title")), series(get_string(dict, "series")),
          publisher(get_string(dict, "publisher")), journal(get_string(dict, "journal")),
          edition(get_string(dict, "edition"))
    {
    }

    misc_t::misc_t(const py::dict &dict)
        : uris(get_vector_string(dict, "uris")), isbns(get_vector_string(dict, "isbns")),
          mirrors(get_vector_string(dict, "mirrors")), origin_plugin(get_string(dict, "origin_plugin"))
    {
    }

    bool exacts_t::operator==(const exacts_t &other) const
    {
        /* extension is checked first, because some sources offer items in multiple
         * formats. */
        return std::tie(extension, ymod, year, volume, number, pages, size) ==
               std::tie(other.extension, other.ymod, other.year, other.volume, other.number, other.pages, other.size);
    }

    bool nonexacts_t::operator==(const nonexacts_t &other) const
    {
        return std::tie(authors, title, series, publisher, journal, edition) ==
               std::tie(other.authors, other.title, other.series, other.publisher, other.journal, other.edition);
    }

    bool misc_t::operator==(const misc_t &other) const
    {
        return std::tie(uris, isbns, mirrors, origin_plugin) ==
               std::tie(other.uris, other.isbns, other.mirrors, other.origin_plugin);
    }

    bool item::operator==(const item &other) const
    {
        return std::tie(nonexacts, exacts, misc) == std::tie(other.nonexacts, other.exacts, other.misc);
    }

    bool item::matches(const item &wanted, const unsigned int fuzzy_min) const
    {
        /* Return false if any exact value doesn't match what's wanted. */
        for (const auto & [ req, got ] : func::zip(wanted.exacts.store, this->exacts.store)) {
            if (req != empty && req != got)
                return false;
        }

        if (wanted.exacts.year != empty) {
            const auto &req = wanted.exacts.year, got = this->exacts.year;

            switch (wanted.exacts.ymod) {
            case year_mod::equal:
                if (!(got == req))
                    return false;
                break;
            case year_mod::eq_gt:
                if (!(got >= req))
                    return false;
                break;
            case year_mod::eq_lt:
                if (!(got <= req))
                    return false;
                break;
            case year_mod::gt:
                if (!(got > req))
                    return false;
                break;
            case year_mod::lt:
                if (!(got < req))
                    return false;
                break;
            case year_mod::unused:
                /* Should never happen. */
                assert(false);
            }
        }

        /* Ad-hoc the file type, for now. */
        if (!wanted.exacts.extension.empty()) {
            if (this->exacts.extension != wanted.exacts.extension)
                return false;
        }

        /* Does the item contain a wanted ISBN? */
        if (!wanted.misc.isbns.empty() && !func::any_intersection(wanted.misc.isbns, this->misc.isbns))
            return false;

        const std::array<string, 3> in_result = {{this->nonexacts.title, this->nonexacts.series, this->nonexacts.publisher}},
                                    requested = {
                                        {wanted.nonexacts.title, wanted.nonexacts.series, wanted.nonexacts.publisher}};

        for (const auto & [ req, got ] : func::zip(requested, in_result)) {
            if (!req.empty()) {
                /*
                 * partial: useful for course literature that can have some
                 * crazy long titles. Also useful for publishers, because
                 * some entries may not use the full name.
                 */
                if (fuzz::partial_ratio(got, req) < fuzzy_min)
                    return false;
            }
        }

        if (!wanted.nonexacts.authors.empty()) {
            unsigned int max_ratio = 0;
            for (const auto & [ req, got ] : algorithm::product(wanted.nonexacts.authors, this->nonexacts.authors)) {
                /*
                 * From some quick testing, it feels like token_set_ratio
                 * works best here.
                 */
                auto ratio = fuzz::token_set_ratio(req, got);
                max_ratio = std::max(ratio, max_ratio);

                if (max_ratio >= fuzzy_min)
                    break;
            }

            if (max_ratio < fuzzy_min)
                return false;
        }

        return true;
    }

} // namespace bookwyrm::core
<commit_msg>fix(item): return empty if dict value cannot be cast to int<commit_after>#include <cctype>

#include <fuzzywuzzy.hpp>

#include "../string.hpp"
#include "algorithm.hpp"
#include "common.hpp"
#include "item.hpp"

namespace bookwyrm::core {

    size_t item::items_idx = 0;

    static int get_integral(const py::dict &dict, const char *key)
    {
        if (!dict.contains(key) || dict[key].is_none())
            return empty;

        try {
            return py::int_(dict[key]);
        } catch (const py::error_already_set &) {
            return empty;
        }
    }

    static string get_string(const py::dict &dict, const char *key)
    {
        if (!dict.contains(key) || dict[key].is_none())
            return "";
        return trim(py::str(dict[key]));
    }

    static vector<string> get_vector_string(const py::dict &dict, const char *key)
    {
        if (!dict.contains(key) || dict[key].is_none())
            return {{}};

        vector<string> strings;
        for (auto &handle : py::list(dict[key]))
            strings.push_back(trim(py::str(handle)));
        return strings;
    }

    exacts_t::exacts_t(const py::dict &dict)
        : ymod(year_mod::unused), year(get_integral(dict, "year")), volume(get_integral(dict, "volume")),
          number(get_integral(dict, "number")), pages(get_integral(dict, "pages")), size(get_integral(dict, "size")),
          extension(get_string(dict, "extension"))
    {
    }

    nonexacts_t::nonexacts_t(const py::dict &dict)
        : authors(get_vector_string(dict, "authors")), title(get_string(dict, "title")), series(get_string(dict, "series")),
          publisher(get_string(dict, "publisher")), journal(get_string(dict, "journal")),
          edition(get_string(dict, "edition"))
    {
    }

    misc_t::misc_t(const py::dict &dict)
        : uris(get_vector_string(dict, "uris")), isbns(get_vector_string(dict, "isbns")),
          mirrors(get_vector_string(dict, "mirrors")), origin_plugin(get_string(dict, "origin_plugin"))
    {
    }

    bool exacts_t::operator==(const exacts_t &other) const
    {
        /* extension is checked first, because some sources offer items in multiple
         * formats. */
        return std::tie(extension, ymod, year, volume, number, pages, size) ==
               std::tie(other.extension, other.ymod, other.year, other.volume, other.number, other.pages, other.size);
    }

    bool nonexacts_t::operator==(const nonexacts_t &other) const
    {
        return std::tie(authors, title, series, publisher, journal, edition) ==
               std::tie(other.authors, other.title, other.series, other.publisher, other.journal, other.edition);
    }

    bool misc_t::operator==(const misc_t &other) const
    {
        return std::tie(uris, isbns, mirrors, origin_plugin) ==
               std::tie(other.uris, other.isbns, other.mirrors, other.origin_plugin);
    }

    bool item::operator==(const item &other) const
    {
        return std::tie(nonexacts, exacts, misc) == std::tie(other.nonexacts, other.exacts, other.misc);
    }

    bool item::matches(const item &wanted, const unsigned int fuzzy_min) const
    {
        /* Return false if any exact value doesn't match what's wanted. */
        for (const auto & [ req, got ] : func::zip(wanted.exacts.store, this->exacts.store)) {
            if (req != empty && req != got)
                return false;
        }

        if (wanted.exacts.year != empty) {
            const auto &req = wanted.exacts.year, got = this->exacts.year;

            switch (wanted.exacts.ymod) {
            case year_mod::equal:
                if (!(got == req))
                    return false;
                break;
            case year_mod::eq_gt:
                if (!(got >= req))
                    return false;
                break;
            case year_mod::eq_lt:
                if (!(got <= req))
                    return false;
                break;
            case year_mod::gt:
                if (!(got > req))
                    return false;
                break;
            case year_mod::lt:
                if (!(got < req))
                    return false;
                break;
            case year_mod::unused:
                /* Should never happen. */
                assert(false);
            }
        }

        /* Ad-hoc the file type, for now. */
        if (!wanted.exacts.extension.empty()) {
            if (this->exacts.extension != wanted.exacts.extension)
                return false;
        }

        /* Does the item contain a wanted ISBN? */
        if (!wanted.misc.isbns.empty() && !func::any_intersection(wanted.misc.isbns, this->misc.isbns))
            return false;

        const std::array<string, 3> in_result = {{this->nonexacts.title, this->nonexacts.series, this->nonexacts.publisher}},
                                    requested = {
                                        {wanted.nonexacts.title, wanted.nonexacts.series, wanted.nonexacts.publisher}};

        for (const auto & [ req, got ] : func::zip(requested, in_result)) {
            if (!req.empty()) {
                /*
                 * partial: useful for course literature that can have some
                 * crazy long titles. Also useful for publishers, because
                 * some entries may not use the full name.
                 */
                if (fuzz::partial_ratio(got, req) < fuzzy_min)
                    return false;
            }
        }

        if (!wanted.nonexacts.authors.empty()) {
            unsigned int max_ratio = 0;
            for (const auto & [ req, got ] : algorithm::product(wanted.nonexacts.authors, this->nonexacts.authors)) {
                /*
                 * From some quick testing, it feels like token_set_ratio
                 * works best here.
                 */
                auto ratio = fuzz::token_set_ratio(req, got);
                max_ratio = std::max(ratio, max_ratio);

                if (max_ratio >= fuzzy_min)
                    break;
            }

            if (max_ratio < fuzzy_min)
                return false;
        }

        return true;
    }

} // namespace bookwyrm::core
<|endoftext|>


<commit_before>#include "ctvm_util.h"

BoostDoubleMatrix CreateRandomMatrix(int rows, int cols){
/*
* Function: CreateRandomMatrix
* ------------------------------
* Allocates a matrix of the specified dimensionality and fills it with random
* entries drawn from a Normal distribution of variance 1.
*
* rows: number of rows in generated matrix
* cols: number of columns in generated matrix
*
* return -- A random matrix of type `matrix<double>`
*/

    /* Set Namespace */
    // using namespace boost::numeric::ublas;

    /* Matrix Allocation */
    BoostDoubleMatrix RandomMatrix (rows,cols);

    /* Random Number Generation */
    boost::posix_time::ptime CurrentTick = boost::posix_time::microsec_clock::local_time();
    boost::mt19937 RNG (static_cast<unsigned int>(CurrentTick.time_of_day().total_milliseconds()));     // Set the integer RNG engine. Time Based Seed.
    boost::normal_distribution<> NormDist (0.0,1.0);   // Specify the distribution with zero mean and unit variance
    boost::variate_generator<boost::mt19937&,
                             boost::normal_distribution<> > RandNormValue(RNG,NormDist);

    /* Loop and Assign */
    for(unsigned i=0; i < RandomMatrix.size1(); ++i){
        for(unsigned j=0; j < RandomMatrix.size2(); ++j){
            RandomMatrix(i,j) = RandNormValue();
        }
    }

    /* Return Filled Random Matrix */
    return RandomMatrix;
}<commit_msg>Commenting to ctvml_util.h<commit_after>#include "ctvm_util.h"

BoostDoubleMatrix CreateRandomMatrix(int rows, int cols){
/*
* Function: CreateRandomMatrix
* ------------------------------
* Allocates a matrix of the specified dimensionality and fills it with random
* entries drawn from a Normal distribution of variance 1.
*
* rows: number of rows in generated matrix
* cols: number of columns in generated matrix
*
* return -- A random matrix of type `matrix<double>`
*/
    /* Matrix Allocation */
    BoostDoubleMatrix RandomMatrix (rows,cols);

    /* Random Number Generation */
    boost::posix_time::ptime CurrentTick = boost::posix_time::microsec_clock::local_time();
    boost::mt19937 RNG (static_cast<unsigned int>(CurrentTick.time_of_day().total_milliseconds()));     // Set the integer RNG engine. Time Based Seed.
    boost::normal_distribution<> NormDist (0.0,1.0);   // Specify the distribution with zero mean and unit variance
    boost::variate_generator<boost::mt19937&,
                             boost::normal_distribution<> > RandNormValue(RNG,NormDist);    // Finally, build the number generator itself.

    /* Loop and Assign */
    for(unsigned i=0; i < RandomMatrix.size1(); ++i){
        for(unsigned j=0; j < RandomMatrix.size2(); ++j){
            RandomMatrix(i,j) = RandNormValue();
        }
    }

    /* Return Filled Random Matrix */
    return RandomMatrix;
}<|endoftext|>