/******************************************************************************/
/*                                                                            */
/*                        X r d O u c R a s h . i c c                         */
/*                                                                            */
/* (c) 2005 by the Board of Trustees of the Leland Stanford, Jr., University  */
/*   Produced by Andrew Hanushevsky for Stanford University under contract    */
/*              DE-AC02-76-SFO0515 with the Department of Energy              */
/*                                                                            */
/* This file is part of the XRootD software suite.                            */
/*                                                                            */
/* XRootD is free software: you can redistribute it and/or modify it under    */
/* the terms of the GNU Lesser General Public License as published by the     */
/* Free Software Foundation, either version 3 of the License, or (at your     */
/* option) any later version.                                                 */
/*                                                                            */
/* XRootD is distributed in the hope that it will be useful, but WITHOUT      */
/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or      */
/* FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public       */
/* License for more details.                                                  */
/*                                                                            */
/* You should have received a copy of the GNU Lesser General Public License   */
/* along with XRootD in a file called COPYING.LESSER (LGPL license) and file  */
/* COPYING (GPL license).  If not, see <http://www.gnu.org/licenses/>.        */
/*                                                                            */
/* The copyright holder's institutional names and contributor's names may not */
/* be used to endorse or promote products derived from this software without  */
/* specific prior written permission of the institution or contributor.       */
/******************************************************************************/

#include <cerrno>

#include "XrdSys/XrdSysPlatform.hh"

/******************************************************************************/
/*                                   A d d                                    */
/******************************************************************************/

template<typename K, typename V>
V *XrdOucRash<K,V>::Add(K KeyVal, V &KeyData, time_t LifeTime,
                       XrdOucRash_Options opt)
{
    time_t lifetime, KeyTime=0;
    XrdOucRash_Item<K,V> *hip;
    XrdOucRash_Tent<K,V> *hiploc;

// Look up the entry. If found, either return an error or delete it
// because caller wanted it replaced or it has expired.
//
   if ((hip = Lookup(KeyVal, &hiploc)))
       {if (opt & Rash_count)
           {hip->Update(hip->Count()+1, 
                       (LifeTime || hip->Time() ? LifeTime + time(0) : 0) );}
        if (!(opt & Rash_replace)
        && ((lifetime=hip->Time())==0||lifetime>=time(0))) return hip->Data();
        hip->Set(KeyData, KeyTime);
        return (V *)0;
       }

// Create a new item
//
   if (LifeTime) KeyTime = LifeTime + time(0);
   if ( !(hip = new XrdOucRash_Item<K,V>(KeyVal, KeyData, KeyTime)) )
      throw ENOMEM;

// Add this item to the table
//
    Insert(KeyVal, hip);
    return (V *)0;
}
  
/******************************************************************************/
/*                                   D e l                                    */
/******************************************************************************/
  
template<typename K, typename V>
int XrdOucRash<K,V>::Del(K KeyVal)
{
    XrdOucRash_Item<K,V> *hip;
    XrdOucRash_Tent<K,V> *hiploc;
    int cnt;

// Look up the entry. If not found, indicate so.
//
   if (!(hip = Lookup(KeyVal, &hiploc))) return -ENOENT;

// Delete the item and return
//
   if ((cnt = hip->Count())) {hip->Update(cnt-1, 0); return cnt;}
   delete hip; 
   hiploc->Item = (XrdOucRash_Item<K,V> *)0;
   rashnum--;
   return 0;
  
}

/******************************************************************************/
/*                                  F i n d                                   */
/******************************************************************************/

template<typename K, typename V>
V *XrdOucRash<K,V>::Find(K KeyVal, time_t *KeyTime)
{
  XrdOucRash_Item<K,V> *hip;
  XrdOucRash_Tent<K,V> *hiploc;
  time_t lifetime = 0;

// Find the entry (remove it if expired and return nothing)
//
   if (!(hip = Lookup(KeyVal, &hiploc))) return (V *)0;
   if ( (lifetime = hip->Time()) && lifetime < time(0) )
      {delete hip;
       hiploc->Item = (XrdOucRash_Item<K,V> *)0;
       rashnum--;
       if (KeyTime) *KeyTime = (time_t)0;
       return (V *)0;
      }

// Return actual information
//
   if (KeyTime) *KeyTime = lifetime;
   return hip->Data();
}

/******************************************************************************/
/*                                 P u r g e                                  */
/******************************************************************************/
  
template<typename K, typename V>
void XrdOucRash<K,V>::Purge()
{
     int i;

// Run through the major table and delete each item in the main table
// the deletion automatically propagates.
//
   for (i = 0; i < 16; i++)
       {if (rashTable[i].Item)
           {delete rashTable[i].Item;  rashTable[i].Item  = 0;}
        if (rashTable[i].Table)
           {delete []rashTable[i].Table; rashTable[i].Table = 0;}
       }
   rashnum = 0;
}
  
/******************************************************************************/
/*                       P r i v a t e   M e t h o d s                        */
/******************************************************************************/
/******************************************************************************/
/*                                 A p p l y                                  */
/******************************************************************************/
  
template<typename K, typename V>
V *XrdOucRash<K,V>::Apply(XrdOucRash_Tent<K,V> *tab,
                          int (*func)(K, V, void *), void *Arg)
{
     int i, rc;
     time_t lifetime;
     XrdOucRash_Item<K,V> *hip;
     V *theVal;

     //Run through all the entries, applying the function to each. Expire
     // dead entries by pretending that the function asked for a deletion.
     //
     for (i = 0; i < 16; i++)
         {if ((hip = tab[i].Item))
             {if ((lifetime = hip->Time()) && lifetime < time(0)) rc = -1;
                 else if ( (rc = (*func)(hip->Key(), *hip->Data(), Arg)) > 0 )
                         return hip->Data();
              if (rc < 0) {delete hip; 
                           tab[i].Item = (XrdOucRash_Item<K,V> *)0;
                           rashnum--;
                          }
             }
          if (tab[i].Table && (theVal = Apply(tab[i].Table, func, Arg)))
             return theVal;
         }
     return (V *)0;
}

/******************************************************************************/
/*                                L o o k u p                                 */
/******************************************************************************/
  
template<typename K, typename V>
XrdOucRash_Item<K,V> *XrdOucRash<K,V>::Lookup(K theKey,
                                              XrdOucRash_Tent<K,V> **tloc)
{
   unsigned long long kVal = key2ull(theKey);
   XrdOucRash_Tent<K,V> *tab = rashTable;
   int j;

// Traverse the binary tree and find the entry
//
   do {j = kVal & 0x0f;
       kVal = kVal >> 4;
      } while(kVal && (tab = tab[j].Table));

// Return result
//
   if (tab)
      {*tloc = &tab[j];
       return tab[j].Item;
      } else {
       *tloc = 0;
       return 0;
     }
}

/******************************************************************************/
/*                                I n s e r t                                 */
/******************************************************************************/
  
template<typename K, typename V>
void XrdOucRash<K,V>::Insert(K theKey, XrdOucRash_Item<K,V> *theItem)
{
   unsigned long long kVal = key2ull(theKey);
   XrdOucRash_Tent<K,V> *tab = rashTable;
   int j;

// Traverse the binary tree and find the entry
//
   do {j = kVal & 0x0f;
       if ((kVal = kVal >> 4))
          {if (tab[j].Table) tab = tab[j].Table;
              else tab = tab[j].Table = new XrdOucRash_Tent<K,V>[16]();
          }
      } while(kVal);

// Insert the entry
//
   tab[j].Item = theItem;
   rashnum++;
}
  
/******************************************************************************/
/*                               k e y 2 u l l                                */
/******************************************************************************/
  
template<typename K, typename V>
unsigned long long XrdOucRash<K,V>::key2ull(K theKey)
{
#ifdef Xrd_Big_Endian
   union {unsigned long long us; K kv[8/sizeof(K)];} Val;
   Val.us = 0;
   Val.kv[8/sizeof(K)-1] = theKey;
#else
   union {unsigned long long us; K kv;} Val;
   Val.us = 0;
   Val.kv = theKey;
#endif
   return Val.us;
}
