<commit_before>/*
    Copyright (C) 2007-2008 by <NAME>

    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; if not, write to the Free
    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#ifndef __CS_CSUTIL_GENERICRESOURCECACHE_H__
#define __CS_CSUTIL_GENERICRESOURCECACHE_H__

#include "csutil/blockallocator.h"
#include "csutil/comparator.h"
#include "csutil/list.h"
#include "csutil/redblacktree.h"

namespace CS
{
  namespace Utility
  {
    /**
     * Mixins intended for use with GenericResourceCache<>.
     */
    namespace ResourceCache
    {
      /// Resource sorting that does not sort
      class SortingNone
      {
      public:
	struct KeyType {};
	  
	template<typename T1, typename T2>
	static bool IsLargerEqual (const T1&, const T2&)
	{
	  return 0;
	}
	template<typename T1, typename T2>
	static bool IsEqual (const T1&, const T2&)
	{
	  return 0;
	}
      };
      
      /**
       * Reuse condition: a resource is reused after a certain time has passed
       */
      template<typename TimeType = uint>
      class ReuseConditionAfterTime
      {
      public:
	struct AddParameter
	{
          /// Lifetime of a resource
	  TimeType lifeTime;
	  
	  AddParameter (TimeType lifeTime = 0) : lifeTime (lifeTime) {}
	};
	struct StoredAuxiliaryInfo
	{
          /// Time after which the resource can be reused
	  TimeType timeToDie;
	  TimeType lifeTime;
	  
	  template<typename ResourceCacheType>
	  StoredAuxiliaryInfo (const ResourceCacheType& cache, 
	    const AddParameter& param) : 
	    lifeTime (param.lifeTime)
	  {}
	};
	
	/// Called when the resource turns "active"
	template<typename ResourceCacheType>
	void MarkActive (const ResourceCacheType& cache,
	  StoredAuxiliaryInfo& elementInfo)
	{
	  elementInfo.timeToDie = cache.GetCurrentTime() + elementInfo.lifeTime;
	}

	template<typename ResourceCacheType>
	bool IsReusable (const ResourceCacheType& cache,
	  const StoredAuxiliaryInfo& elementInfo,
	  const typename ResourceCacheType::CachedType& data)
	{
	  return cache.GetCurrentTime() > elementInfo.timeToDie;
	}
      };

      /// Reuse condition: a resource is reused after being flagged as such
      class ReuseConditionFlagged
      {
      public:
	struct AddParameter
	{
	  AddParameter () {}
	};
	struct StoredAuxiliaryInfo
	{
          /// Whether a resource can be reused.
	  bool reusable;
	  
	  template<typename ResourceCacheType>
	  StoredAuxiliaryInfo (const ResourceCacheType& cache, 
	    const AddParameter& param) : reusable (false)
	  {}
	};
	
	
	/// Called when the resource turns "active"
	template<typename ResourceCacheType>
	void MarkActive (const ResourceCacheType& cache,
	  StoredAuxiliaryInfo& elementInfo)
	{
	  // Reset flag
	  elementInfo.reusable = false;
	}

	template<typename ResourceCacheType>
	bool IsReusable (const ResourceCacheType& cache,
	  const StoredAuxiliaryInfo& elementInfo,
	  const typename ResourceCacheType::CachedType& data)
	{
	  return elementInfo.reusable;
	}
      };
      
      /**
       * Purge condition: a resource is purged after a certain time has passed
       */
      template<typename TimeType = uint>
      class PurgeConditionAfterTime
      {
	/**
	  * Time interval since last use after which a resource is considered
	  * "aged" and may be purged
	  */
	TimeType purgeAge;
      public:
	struct AddParameter
	{
	  AddParameter () {}
	};
	struct StoredAuxiliaryInfo
	{
	  TimeType lastTimeUsed;
	  
	  template<typename ResourceCacheType>
	  StoredAuxiliaryInfo (const ResourceCacheType& cache, 
	    const AddParameter& param) {}
	};
	
	PurgeConditionAfterTime (TimeType purgeAge = 60)
	  : purgeAge (purgeAge) {}
	
	/// Called when the resource turns "active"
	template<typename ResourceCacheType>
	void MarkActive (const ResourceCacheType& cache,
	  StoredAuxiliaryInfo& elementInfo)
	{
	  elementInfo.lastTimeUsed = cache.GetCurrentTime();
	}

	template<typename ResourceCacheType>
	bool IsPurgeable (const ResourceCacheType& cache,
	  const StoredAuxiliaryInfo& elementInfo,
	  const typename ResourceCacheType::CachedType& data)
	{
	  return (cache.GetCurrentTime() > 
	    elementInfo.lastTimeUsed + purgeAge);
	}
      };

    } // namespace ResourceCache
    
    /**
     * Generic cache for resources.
     * It can cache frequently needed, reuseable data, can fetch cached 
     * resources based on a given sorting (not just exact matches, also 
     * resources that are bigger than some given key) and free resources that
     * were cached but not used for some time.
     */
    template<typename T,
      typename _TimeType = uint, 
      typename _ResourceSorting = ResourceCache::SortingNone,
      typename _ReuseCondition = ResourceCache::ReuseConditionAfterTime<_TimeType>,
      typename _PurgeCondition = ResourceCache::PurgeConditionAfterTime<_TimeType> >
    class GenericResourceCache
    {
    public:
      typedef T CachedType;
      typedef _TimeType TimeType;
      typedef _ResourceSorting ResourceSorting;
      typedef _ReuseCondition ReuseCondition;
      typedef _PurgeCondition PurgeCondition;
      
    protected:
      typedef typename ResourceSorting::KeyType ResourceSortingKeyType;
      typedef typename ReuseCondition::AddParameter ReuseConditionAddParameter;
      typedef typename ReuseCondition::StoredAuxiliaryInfo ReuseConditionAuxiliary;
      typedef typename PurgeCondition::AddParameter PurgeConditionAddParameter;
      typedef typename PurgeCondition::StoredAuxiliaryInfo PurgeConditionAuxiliary;

      template<typename Super>
      struct DataStorage : public CS::Memory::CustomAllocatedDerived<Super>
      {
	T data;
	
	template<typename A>
	DataStorage (const T& data, const A& a) : 
          CS::Memory::CustomAllocatedDerived<Super> (a), data (data) {}
      };
      struct Element : public DataStorage<ReuseConditionAuxiliary>,
                       public PurgeConditionAuxiliary
      {
	
	
	Element (const T& data,
	  const ReuseConditionAuxiliary& reuseAux,
	  const PurgeConditionAuxiliary& purgeAux)
	  : DataStorage<ReuseConditionAuxiliary> (data, reuseAux),
	    PurgeConditionAuxiliary (purgeAux)
	{
	}
	
	ReuseConditionAuxiliary& GetReuseAuxiliary()
	{ return *(static_cast<ReuseConditionAuxiliary*> (this)); }
	const ReuseConditionAuxiliary& GetReuseAuxiliary() const
	{ return *(static_cast<const ReuseConditionAuxiliary*> (this)); }
        
	PurgeConditionAuxiliary& GetPurgeAuxiliary()
	{ return *(static_cast<PurgeConditionAuxiliary*> (this)); }
	const PurgeConditionAuxiliary& GetPurgeAuxiliary() const
	{ return *(static_cast<const PurgeConditionAuxiliary*> (this)); }
      };
      
      struct ElementWrapper
      {
	Element* ptr;
	
	ElementWrapper () {}
	ElementWrapper (Element* ptr) : ptr (ptr) {}
	ElementWrapper (const ElementWrapper& other) : ptr (other.ptr) { }
	
	Element* operator->() { return ptr; }
	operator Element*() const { return ptr; }
	
	// FIXME: Comparisons are fragile, rely on csComparator only using <
	bool operator== (const ElementWrapper& other) const
	{ return ptr == other.ptr; }
	
	bool operator< (const ElementWrapper& other) const
	{
	  // @@@ Make more efficient...
	  if (ResourceSorting::IsEqual (ptr->data, other.ptr->data)) return false;
	  if (ResourceSorting::IsLargerEqual (ptr->data, other.ptr->data)) return false;
	  return true;
	}
	bool operator< (const ResourceSortingKeyType& other) const
	{
	  // @@@ Make more efficient...
	  if (ResourceSorting::IsEqual (ptr->data, other)) return false;
	  if (ResourceSorting::IsLargerEqual (ptr->data, other)) return false;
	  return true;
	}
	friend bool operator< (const ResourceSortingKeyType& key, 
			       const ElementWrapper& el)
	{
	  // @@@ Make more efficient...
	  if (ResourceSorting::IsEqual (el.ptr->data, key)) return false;
	  if (ResourceSorting::IsLargerEqual (el.ptr->data, key)) return true;
	  return false;
	}
	
      };
      
      csBlockAllocator<Element> elementAlloc;
      // Tree of available resources
      struct AvailableResourcesWrapper : public ReuseCondition
      {
      private:
	csBlockAllocator<Element>& elementAlloc;
	struct RBTraverser
	{
	  csBlockAllocator<Element>& elementAlloc;
	  
	  RBTraverser (csBlockAllocator<Element>& elementAlloc) :
	    elementAlloc (elementAlloc) {}
	  
	  void operator() (Element* el)
	  {
	    elementAlloc.Free (el);
	  }
	};
      public:
	csRedBlackTree<ElementWrapper> v;
        
        AvailableResourcesWrapper (const ReuseCondition& other,
          csBlockAllocator<Element>& elementAlloc)
          : ReuseCondition (other), elementAlloc (elementAlloc) { }
	
	void Destroy()
	{
	  RBTraverser trav (elementAlloc);
	  v.TraverseInOrder (trav);
	  v.DeleteAll();
	}
      } availableResources;
      // List of active resources
      struct ActiveResourcesWrapper : public PurgeCondition
      {
      protected:
	csBlockAllocator<Element>& elementAlloc;
      public:
	csList<ElementWrapper> v;
        
        ActiveResourcesWrapper (const PurgeCondition& other,
          csBlockAllocator<Element>& elementAlloc)
	  : PurgeCondition (other), elementAlloc (elementAlloc) { }
	
	void Destroy()
	{
	  typename csList<ElementWrapper>::Iterator listIt (v);
	  while (listIt.HasNext())
	  {
	    Element* el = listIt.Next();
	    elementAlloc.Free (el);
	  }
	  v.DeleteAll();
	}
      } activeResources;
      
      TimeType currentTime;
      /**
       * The next time when the momentarily available resources should be 
       * scanned for "aged" (long time unused) resources that can be freed */
      TimeType nextPurgeAged;
      /// Whether a "clear" is pending.
      bool clearReq;
      
      ReuseCondition& GetReuseCondition()
      { return *(static_cast<ReuseCondition*> (&availableResources)); }
      PurgeCondition& GetPurgeCondition()
      { return *(static_cast<PurgeCondition*> (&activeResources)); }
      
      class SearchDataTraverser
      {
	T* entry;
	Element*& ret;
	
      public:
	SearchDataTraverser (T* entry, Element*& ret) 
	  : entry (entry), ret (ret) {}
	
        bool operator() (Element* el)
	{
	  if (&(el->data) == entry)
	  {
	    ret = el;
	    return false;
	  }
	  return true;
	}
      };
      
      Element* ElementFromData (T* entry)
      {
	// Given some cached data, obtain the Element from it.
	
	/* @@@ Right now the only way is to search both the active and
	 * available resource lists. */
	
	// Search active resource list.
	typename csList<ElementWrapper>::Iterator listIt (activeResources.v);
	while (listIt.HasNext())
	{
	  Element* el = listIt.Next();
	  if (&(el->data) == entry) return el;
	}
	
	// Search available resource tree.
	Element* treeSearchRet = 0;
	SearchDataTraverser sdt (entry, treeSearchRet);
	availableResources.v.TraverseInOrder (sdt);
	if (treeSearchRet != 0) return treeSearchRet;
	
	CS_ASSERT_MSG("Element is not from this resource cache", false);
	return 0;
      }
    public:
      /// Interval for the aged resource scan
      TimeType agedPurgeInterval;
    
      GenericResourceCache (const ReuseCondition& reuse = ReuseCondition(),
        const PurgeCondition& purge = PurgeCondition()) : 
	availableResources (reuse, elementAlloc),
        activeResources (purge, elementAlloc), 
        currentTime (0), nextPurgeAged (0),
	clearReq (false), agedPurgeInterval (60)
      {
      }
      
      ~GenericResourceCache()
      {
      }

      /**
       * Clear all cached resources. \a instaClear immediately clears all
       * resources when set, if not, clearing is delayed until the next
       * AdvanceTime() call (useful when resources may still be in use).
       */
      void Clear (bool instaClear = false)
      {
	if (instaClear)
	{
	  availableResources.Destroy ();
	  activeResources.Destroy ();
	  clearReq = false;
	}
	else
	  // Don't clear just yet, rather, clear when we advance the next time
	  clearReq = true;
      }
      
    protected:
      class PurgeTraverser
      {
	csArray<ElementWrapper>& purgeList;
	PurgeCondition& purgeCnd;
	GenericResourceCache& cache;
	
      public:
	PurgeTraverser (csArray<ElementWrapper>& purgeList,
			PurgeCondition& purgeCnd,
			GenericResourceCache& cache) 
	  : purgeList (purgeList), purgeCnd (purgeCnd), cache (cache) {}
      
	void operator() (ElementWrapper el)
	{
	  if (purgeCnd.IsPurgeable (cache, el->GetPurgeAuxiliary(),
	      el->data))
	  {
	    purgeList.Push (el);
	  }
	}
      };
    
    public:
      /**
       * Advance the time kept by the cache. Determines what resources
       * can be reused or even freed.
       */
      void AdvanceTime (TimeType time)
      {
	if (clearReq)
	{
	  Clear (true);
	  clearReq = false;
	}
	
	currentTime = time;
	
	if (time >= nextPurgeAged)<commit_msg>Make GenericResourceCache actually compile.<commit_after>/*
    Copyright (C) 2007-2008 by <NAME>

    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; if not, write to the Free
    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#ifndef __CS_CSUTIL_GENERICRESOURCECACHE_H__
#define __CS_CSUTIL_GENERICRESOURCECACHE_H__

#include "csutil/blockallocator.h"
#include "csutil/comparator.h"
#include "csutil/list.h"
#include "csutil/redblacktree.h"

namespace CS
{
  namespace Utility
  {
    /**
     * Mixins intended for use with GenericResourceCache<>.
     */
    namespace ResourceCache
    {
      /// Resource sorting that does not sort
      class SortingNone
      {
      public:
	struct KeyType {};
	  
	template<typename T1, typename T2>
	static bool IsLargerEqual (const T1&, const T2&)
	{
	  return 0;
	}
	template<typename T1, typename T2>
	static bool IsEqual (const T1&, const T2&)
	{
	  return 0;
	}
      };
      
      /**
       * Reuse condition: a resource is reused after a certain time has passed
       */
      template<typename TimeType = uint>
      class ReuseConditionAfterTime
      {
      public:
	struct AddParameter
	{
          /// Lifetime of a resource
	  TimeType lifeTime;
	  
	  AddParameter (TimeType lifeTime = 0) : lifeTime (lifeTime) {}
	};
	struct StoredAuxiliaryInfo
	{
          /// Time after which the resource can be reused
	  TimeType timeToDie;
	  TimeType lifeTime;
	  
	  template<typename ResourceCacheType>
	  StoredAuxiliaryInfo (const ResourceCacheType& cache, 
	    const AddParameter& param) : 
	    lifeTime (param.lifeTime)
	  {}
	};
	
	/// Called when the resource turns "active"
	template<typename ResourceCacheType>
	void MarkActive (const ResourceCacheType& cache,
	  StoredAuxiliaryInfo& elementInfo)
	{
	  elementInfo.timeToDie = cache.GetCurrentTime() + elementInfo.lifeTime;
	}

	template<typename ResourceCacheType>
	bool IsReusable (const ResourceCacheType& cache,
	  const StoredAuxiliaryInfo& elementInfo,
	  const typename ResourceCacheType::CachedType& data)
	{
	  return cache.GetCurrentTime() > elementInfo.timeToDie;
	}
      };

      /// Reuse condition: a resource is reused after being flagged as such
      class ReuseConditionFlagged
      {
      public:
	struct AddParameter
	{
	  AddParameter () {}
	};
	struct StoredAuxiliaryInfo
	{
          /// Whether a resource can be reused.
	  bool reusable;
	  
	  template<typename ResourceCacheType>
	  StoredAuxiliaryInfo (const ResourceCacheType& cache, 
	    const AddParameter& param) : reusable (false)
	  {}
	};
	
	
	/// Called when the resource turns "active"
	template<typename ResourceCacheType>
	void MarkActive (const ResourceCacheType& cache,
	  StoredAuxiliaryInfo& elementInfo)
	{
	  // Reset flag
	  elementInfo.reusable = false;
	}

	template<typename ResourceCacheType>
	bool IsReusable (const ResourceCacheType& cache,
	  const StoredAuxiliaryInfo& elementInfo,
	  const typename ResourceCacheType::CachedType& data)
	{
	  return elementInfo.reusable;
	}
      };
      
      /**
       * Purge condition: a resource is purged after a certain time has passed
       */
      template<typename TimeType = uint>
      class PurgeConditionAfterTime
      {
	/**
	  * Time interval since last use after which a resource is considered
	  * "aged" and may be purged
	  */
	TimeType purgeAge;
      public:
	struct AddParameter
	{
	  AddParameter () {}
	};
	struct StoredAuxiliaryInfo
	{
	  TimeType lastTimeUsed;
	  
	  template<typename ResourceCacheType>
	  StoredAuxiliaryInfo (const ResourceCacheType& cache, 
	    const AddParameter& param) {}
	};
	
	PurgeConditionAfterTime (TimeType purgeAge = 60)
	  : purgeAge (purgeAge) {}
	
	/// Called when the resource turns "active"
	template<typename ResourceCacheType>
	void MarkActive (const ResourceCacheType& cache,
	  StoredAuxiliaryInfo& elementInfo)
	{
	  elementInfo.lastTimeUsed = cache.GetCurrentTime();
	}

	template<typename ResourceCacheType>
	bool IsPurgeable (const ResourceCacheType& cache,
	  const StoredAuxiliaryInfo& elementInfo,
	  const typename ResourceCacheType::CachedType& data)
	{
	  return (cache.GetCurrentTime() > 
	    elementInfo.lastTimeUsed + purgeAge);
	}
      };

    } // namespace ResourceCache
    
    /**
     * Generic cache for resources.
     * It can cache frequently needed, reuseable data, can fetch cached 
     * resources based on a given sorting (not just exact matches, also 
     * resources that are bigger than some given key) and free resources that
     * were cached but not used for some time.
     */
    template<typename T,
      typename _TimeType = uint, 
      typename _ResourceSorting = ResourceCache::SortingNone,
      typename _ReuseCondition = ResourceCache::ReuseConditionAfterTime<_TimeType>,
      typename _PurgeCondition = ResourceCache::PurgeConditionAfterTime<_TimeType> >
    class GenericResourceCache
    {
    public:
      typedef T CachedType;
      typedef _TimeType TimeType;
      typedef _ResourceSorting ResourceSorting;
      typedef _ReuseCondition ReuseCondition;
      typedef _PurgeCondition PurgeCondition;
      
    protected:
      typedef typename ResourceSorting::KeyType ResourceSortingKeyType;
      typedef typename ReuseCondition::AddParameter ReuseConditionAddParameter;
      typedef typename ReuseCondition::StoredAuxiliaryInfo ReuseConditionAuxiliary;
      typedef typename PurgeCondition::AddParameter PurgeConditionAddParameter;
      typedef typename PurgeCondition::StoredAuxiliaryInfo PurgeConditionAuxiliary;

      template<typename Super>
      struct DataStorage : public CS::Memory::CustomAllocatedDerived<Super>
      {
	T data;
	
	template<typename A>
	DataStorage (const T& data, const A& a) : 
          CS::Memory::CustomAllocatedDerived<Super> (a), data (data) {}
      };
      struct Element : public DataStorage<ReuseConditionAuxiliary>,
                       public PurgeConditionAuxiliary
      {
	
	
	Element (const T& data,
	  const ReuseConditionAuxiliary& reuseAux,
	  const PurgeConditionAuxiliary& purgeAux)
	  : DataStorage<ReuseConditionAuxiliary> (data, reuseAux),
	    PurgeConditionAuxiliary (purgeAux)
	{
	}
	
	ReuseConditionAuxiliary& GetReuseAuxiliary()
	{ return *(static_cast<ReuseConditionAuxiliary*> (this)); }
	const ReuseConditionAuxiliary& GetReuseAuxiliary() const
	{ return *(static_cast<const ReuseConditionAuxiliary*> (this)); }
        
	PurgeConditionAuxiliary& GetPurgeAuxiliary()
	{ return *(static_cast<PurgeConditionAuxiliary*> (this)); }
	const PurgeConditionAuxiliary& GetPurgeAuxiliary() const
	{ return *(static_cast<const PurgeConditionAuxiliary*> (this)); }
      };
      
      struct ElementWrapper
      {
	Element* ptr;
	
	ElementWrapper () {}
	ElementWrapper (Element* ptr) : ptr (ptr) {}
	ElementWrapper (const ElementWrapper& other) : ptr (other.ptr) { }
	
	Element* operator->() { return ptr; }
	operator Element*() const { return ptr; }
	
	// FIXME: Comparisons are fragile, rely on csComparator only using <
	bool operator== (const ElementWrapper& other) const
	{ return ptr == other.ptr; }
	
	bool operator< (const ElementWrapper& other) const
	{
	  // @@@ Make more efficient...
	  if (ResourceSorting::IsEqual (ptr->data, other.ptr->data)) return false;
	  if (ResourceSorting::IsLargerEqual (ptr->data, other.ptr->data)) return false;
	  return true;
	}
	bool operator< (const ResourceSortingKeyType& other) const
	{
	  // @@@ Make more efficient...
	  if (ResourceSorting::IsEqual (ptr->data, other)) return false;
	  if (ResourceSorting::IsLargerEqual (ptr->data, other)) return false;
	  return true;
	}
	friend bool operator< (const ResourceSortingKeyType& key, 
			       const ElementWrapper& el)
	{
	  // @@@ Make more efficient...
	  if (ResourceSorting::IsEqual (el.ptr->data, key)) return false;
	  if (ResourceSorting::IsLargerEqual (el.ptr->data, key)) return true;
	  return false;
	}
	
      };
      
      csBlockAllocator<Element> elementAlloc;
      // Tree of available resources
      struct AvailableResourcesWrapper : public ReuseCondition
      {
      private:
	csBlockAllocator<Element>& elementAlloc;
	struct RBTraverser
	{
	  csBlockAllocator<Element>& elementAlloc;
	  
	  RBTraverser (csBlockAllocator<Element>& elementAlloc) :
	    elementAlloc (elementAlloc) {}
	  
	  void Process (Element* el)
	  {
	    elementAlloc.Free (el);
	  }
	};
      public:
	csRedBlackTree<ElementWrapper> v;
        
        AvailableResourcesWrapper (const ReuseCondition& other,
          csBlockAllocator<Element>& elementAlloc)
          : ReuseCondition (other), elementAlloc (elementAlloc) { }
	
	void Destroy()
	{
	  RBTraverser trav (elementAlloc);
	  v.TraverseInOrder (trav);
	  v.DeleteAll();
	}
      } availableResources;
      // List of active resources
      struct ActiveResourcesWrapper : public PurgeCondition
      {
      protected:
	csBlockAllocator<Element>& elementAlloc;
      public:
	csList<ElementWrapper> v;
        
        ActiveResourcesWrapper (const PurgeCondition& other,
          csBlockAllocator<Element>& elementAlloc)
	  : PurgeCondition (other), elementAlloc (elementAlloc) { }
	
	void Destroy()
	{
	  typename csList<ElementWrapper>::Iterator listIt (v);
	  while (listIt.HasNext())
	  {
	    Element* el = listIt.Next();
	    elementAlloc.Free (el);
	  }
	  v.DeleteAll();
	}
      } activeResources;
      
      TimeType currentTime;
      /**
       * The next time when the momentarily available resources should be 
       * scanned for "aged" (long time unused) resources that can be freed */
      TimeType nextPurgeAged;
      /// Whether a "clear" is pending.
      bool clearReq;
      
      ReuseCondition& GetReuseCondition()
      { return *(static_cast<ReuseCondition*> (&availableResources)); }
      PurgeCondition& GetPurgeCondition()
      { return *(static_cast<PurgeCondition*> (&activeResources)); }
      
      class SearchDataTraverser
      {
	T* entry;
	Element*& ret;
	
      public:
	SearchDataTraverser (T* entry, Element*& ret) 
	  : entry (entry), ret (ret) {}
	
        bool Process (Element* el)
	{
	  if (&(el->data) == entry)
	  {
	    ret = el;
	    return false;
	  }
	  return true;
	}
      };
      
      Element* ElementFromData (T* entry)
      {
	// Given some cached data, obtain the Element from it.
	
	/* @@@ Right now the only way is to search both the active and
	 * available resource lists. */
	
	// Search active resource list.
	typename csList<ElementWrapper>::Iterator listIt (activeResources.v);
	while (listIt.HasNext())
	{
	  Element* el = listIt.Next();
	  if (&(el->data) == entry) return el;
	}
	
	// Search available resource tree.
	Element* treeSearchRet = 0;
	SearchDataTraverser sdt (entry, treeSearchRet);
	availableResources.v.TraverseInOrder (sdt);
	if (treeSearchRet != 0) return treeSearchRet;
	
	CS_ASSERT_MSG("Element is not from this resource cache", false);
	return 0;
      }
    public:
      /// Interval for the aged resource scan
      TimeType agedPurgeInterval;
    
      GenericResourceCache (const ReuseCondition& reuse = ReuseCondition(),
        const PurgeCondition& purge = PurgeCondition()) : 
	availableResources (reuse, elementAlloc),
        activeResources (purge, elementAlloc), 
        currentTime (0), nextPurgeAged (0),
	clearReq (false), agedPurgeInterval (60)
      {
      }
      
      ~GenericResourceCache()
      {
      }

      /**
       * Clear all cached resources. \a instaClear immediately clears all
       * resources when set, if not, clearing is delayed until the next
       * AdvanceTime() call (useful when resources may still be in use).
       */
      void Clear (bool instaClear = false)
      {
	if (instaClear)
	{
	  availableResources.Destroy ();
	  activeResources.Destroy ();
	  clearReq = false;
	}
	else
	  // Don't clear just yet, rather, clear when we advance the next time
	  clearReq = true;
      }
      
      /**
       * Advance the time kept by the cache. Determines what resources
       * can be reused or even freed.
       */
      void AdvanceTime (TimeType time)
      {
	if (clearReq)
	{
	  Clear (true);
	  clearReq = false;
	}
	
	currentTime = time;
	
	if (time >= nextPurgeAged)
{'fraction_non_alphanumeric': 0.08874375720322704, 'fraction_numerical': 0.0028428736073761044, 'mean_word_length': 3.17698973042362, 'pattern_counts': {'":': 0, '<': 95, '<?xml version=': 0, '>': 117, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2748681', 'n_tokens_mistral': 8005, 'n_tokens_neox': 7449, 'n_words': 2189}