Program Listing for File CachingOutletWrapper.hpp

Return to documentation for file (include/uit/spouts/wrappers/outlet/CachingOutletWrapper.hpp)

#pragma once
#ifndef UIT_SPOUTS_WRAPPERS_OUTLET_CACHINGOUTLETWRAPPER_HPP_INCLUDE
#define UIT_SPOUTS_WRAPPERS_OUTLET_CACHINGOUTLETWRAPPER_HPP_INCLUDE

#include <cstddef>

#include "../../../../../third-party/Empirical/source/base/optional.h"
#include "../../../../../third-party/Empirical/source/tools/QueueCache.h"

#include "../../../../uitsl/debug/WarnOnce.hpp"
#include "../../../../uitsl/distributed/CachePacket.hpp"

namespace uit {
namespace internal {

template<typename Outlet>
class CachingOutletWrapper {

  Outlet outlet;

  using ImplSpec = typename Outlet::ImplSpec;

  using this_t = CachingOutletWrapper<Outlet>;
  using value_type = typename ImplSpec::value_type;

  emp::QueueCache<
    size_t,
    value_type,
    ImplSpec::SpoutCacheSize
  > cache;

  void CacheCurrent() {
    const auto& packet = outlet.Get();
    if ( packet.HasData() ) cache.Put( packet.GetID(), packet.GetData() );
    else if ( cache.Contains(packet.GetID()) ) cache.Get( packet.GetID() );
    //^ Get moves item to the front of the cache
    else {
      static const uitsl::WarnOnce w{ "missing cache packet" };
    }
  }

  size_t DoProcTryStep(const size_t num_steps=1) {

    size_t num_steps_countdown{ num_steps };

    while ( num_steps_countdown && outlet.TryStep( 1 ) ) {
      --num_steps_countdown;
      CacheCurrent();
    }

    return num_steps - num_steps_countdown;
  }

  size_t DoProcJump() {
    if ( CanStep() ) return DoProcTryStep( std::numeric_limits<size_t>::max() );
    else {
      const size_t res{ outlet.Jump() };
      CacheCurrent();
      return res;
    }
  }

  const value_type& DoProcGet() const {
    const static value_type pristine{};
    if ( cache.Empty() ) return pristine;
    else return cache.begin()->second;
  }

  value_type& DoProcGet() {
    const static uitsl::WarnOnce w{ "calling mutable Get() incurs extra copy" };

    thread_local value_type mutable_copy;
    mutable_copy = const_cast<const this_t*>(this)->DoProcGet();
    return mutable_copy;
  }


public:

  CachingOutletWrapper(CachingOutletWrapper& other) = default;

  CachingOutletWrapper(const CachingOutletWrapper& other) = default;

  CachingOutletWrapper(CachingOutletWrapper&& other) = default;

  template <typename... Args>
  CachingOutletWrapper(Args&&... args)
  : outlet(std::forward<Args>(args)...)
  { ; }

  size_t TryStep(const size_t num_steps) {
    if ( HoldsProcImpl().value_or( true ) ) return DoProcTryStep( num_steps );
    else return outlet.TryStep( num_steps );
  }

  size_t Jump() {
    if ( HoldsProcImpl().value_or( true ) ) return DoProcJump();
    else return outlet.Jump();
  }

  const value_type& Get() const {
    if ( HoldsProcImpl().value_or( true ) ) return DoProcGet();
    else return outlet.Get().GetData();
  }

  value_type& Get() {
    if ( HoldsProcImpl().value_or( true ) ) return DoProcGet();
    else return outlet.Get().GetData();
  }

  const value_type& JumpGet() { Jump(); return Get(); }

  const value_type& GetNext() { while (TryStep() == 0); return Get(); }

  using optional_ref_t = emp::optional<std::reference_wrapper<
    const value_type
  >>;

  optional_ref_t GetNextOrNullopt() {
  return TryStep()
    ? optional_ref_t{ std::reference_wrapper{ Get() } }
    : std::nullopt;
  }

  size_t GetReadCount() const { return outlet.GetReadCount(); }

  size_t GetRevisionCount() const { return outlet.GetRevisionCount(); }

  size_t GetNetFlux() const { return outlet.GetNetFlux(); }

  template <typename WhichDuct, typename... Args>
  void EmplaceDuct(Args&&... args) {
    outlet.template EmplaceDuct<WhichDuct>( std::forward<Args>(args)... );
  }

  template <typename WhichDuct, typename... Args>
  void SplitDuct(Args&&... args) {
    outlet.template SplitDuct<WhichDuct>( std::forward<Args>(args)... );
  }

  auto GetDuctUID() const { return outlet.GetUID(); }

  emp::optional<bool> HoldsIntraImpl() const { return outlet.HoldsIntraImpl(); }

  emp::optional<bool> HoldsThreadImpl() const {
    return outlet.HoldsThreadImpl();
  }

  emp::optional<bool> HoldsProcImpl() const { return outlet.HoldsProcImpl(); }

  bool CanStep() const { return outlet.CanStep(); }

};

} // namespace internal
} // namespace uit

#endif // #ifndef UIT_SPOUTS_WRAPPERS_OUTLET_CACHINGOUTLETWRAPPER_HPP_INCLUDE