/* mapping.hpp -- an associative array that maps strings into arbitrary fixed-length values
* by [email protected] at Sat Mar 23 16:01:55 CET 2002
*/

#ifdef __GNUC__
#ifndef __clang__
#pragma interface
#endif
#endif

#ifndef MAPPING_HPP
#define MAPPING_HPP 1

#include "config2.h"
#include "gensi.hpp"

class Mapping {
 /** A mapping is an assicative array whose keys are arbitrary binary strings
  * and its values are arbirary fixed-length data. The Mapping class is
  * abstract. The longest key which can be stored in a Mapping::Gen has
  * length `(slen_t)-3'. The caller must verify that she doesn't try to
  * call us with a longer string.
  */
 class Gen {
  public:
   inline virtual ~Gen() {}
   /** Returns the length of one data value (same for all values, determined
    * at construction of Gen).
    */
   inline slen_t getDataLen() const { return datalen; }
   /** Returns the number of keys currently in this Mapping. */
   inline slen_t getLength() const { return len; }
   /** Returns the number of key positions allocated for this Mapping. This
    * number may or may not have meaning, depending on the implementation.
    */
   inline slen_t getAlloced() const { return len; }
   /**
    * Updates or extends the mapping with the specified key/value pair.
    * @param data must point to an area of length getDataLen(). That
    *        area will be copied (memcpy()) by set().
    * @return true if key previously hasn't existed
    */
   virtual bool set(char const*key, slen_t keylen, char const*data) =0;
   /** @return the address of the data that corresponds to param
    * `key', or NULLP if `key' was not found. The returned value is valid
    * until the next modification (.set(), deletee() methods).
    */
   virtual char* get(char const*key, slen_t keylen) =0;
   /**
    * Updates mapping with the specified key/value pair. Leaves it unchanged
    * if the specified key doesn't exist. Calls get() and memcpy() to do the
    * job.
    * @param data must point to an area of length getDataLen(). That
    *        area will be copied by set().
    * @return true if key previously hasn't existed
    */
   bool update(char const*key, slen_t keylen, char const*data);

   /** Calls get to do the job. */
   inline bool exists(char const*key, slen_t keylen) { return NULLP!=get(key, keylen); }

   /**
    * Deletes the specified `key' from the mapping.
    * @param data must point to an area of length getDataLen(). That
    *        area will be copied by set().
    * @return true if key previously hasn't existed
    */
   virtual bool deletee(char const*key, slen_t keylen) =0;

   /** The user must ensure that she isn't updating the mapping while walking.
    * @param key (out) is set to NULLP on empty mapping.
    */
   virtual void getFirst(char const*const*& key, slen_t &keylen, char *& data) =0;
   /** The user must ensure that she isn't updating the mapping while walking.
    * @param key (out) is set to NULLP if there are no more elements
    */
   virtual void getNext (char const*const*& key, slen_t &keylen, char *& data) =0;

  protected:
   slen_t datalen, len, alloced;
 };

 /** Double hashing. Still abstract. */
 class DoubleHash: public Gen {
  public:
   inline virtual ~DoubleHash() {}
   /** If it modifies `scale', then it must modify alloced, may modify minlen
    * and maxused, and must not modify anything else. If it does not modify
    * `scale', it must not modify anything else. After it returns,
    * obj_assert() must hold. Must not copy or (re)allocate memory,
    * rehashing is not done by vi_scale(). rehash() calls vi_scale(), and
    * rehash() does the real reallocation.
    */
   virtual void vi_scale() =0;
   /** First hash function.  Must return a value in 0..alloced-1 */
   virtual slen_t vi_h1(char const*key, slen_t keylen) =0;
   /** Second hash function. Must return a value in 1..alloced-1, which is
    * realatively prime to the value returned by vi_h2().
    */
   virtual slen_t vi_h2(char const*key, slen_t keylen) =0;
   /** Destruct and free resources used by data. Called by deletee(). */
   virtual void vi_dtor(char *data) =0;

   /** Called by set() and deletee() ?? */
   virtual bool set     (char const*  key, slen_t  keylen, char const*data);
   virtual char*get     (char const*  key, slen_t  keylen);
   virtual bool deletee (char const*  key, slen_t  keylen);
   virtual void getFirst(char const*const*&key, slen_t &keylen, char *& data);
   virtual void getNext (char const*const*&key, slen_t &keylen, char *& data);
   /** Delete everything. */
   void clear();
  protected:
   void rehash();
   /** @return true */
   bool obj_assert();
   /** `keylen==(slen_t)-1' indicates a place never used,
    *  `keylen==(slen_t)-2' indicates a deleted place
    * Before changing the value of NEVER_USED, please update the memset(...)
    * in rehash().
    */
   BEGIN_STATIC_ENUM1(slen_t) NEVER_USED=(slen_t)-1, DELETED=(slen_t)-2 END_STATIC_ENUM()
   // static const slen_t NEVER_USED=(slen_t)-1, DELETED=(slen_t)-2;
   struct Ary {
     slen_t keylen;
     /** data is located at keydata, key is located keydata-datalen */
     char *keydata;
   } *ary;
   /* The minimum number of keys before rehashing. */
   slen_t minlen;
   /* The maximum number of keys before rehashing. */
   slen_t maxused;
   /* The number of used places. A place is used unless it is NEVER_USED */
   slen_t used;
   /** Used by the implementor of vi_scale. */
   unsigned scale;
 };

 /** Simple prime modulus double hashing with a factor of around 1.5
  * between `alloced' scales. This class is not abstract anymore.
  */
 class DoubleHash15: public DoubleHash {
  public:
   DoubleHash15(slen_t datalen_);
   virtual ~DoubleHash15();
   virtual void vi_scale();
   /** @return key % Prime */
   virtual slen_t vi_h1(char const*key, slen_t keylen);
   /** @return (key % (Prime-1))+1 */
   virtual slen_t vi_h2(char const*key, slen_t keylen);
   /** No-op. */
   virtual void vi_dtor(char *data);
 };
public:
 typedef DoubleHash15 H;
};

#endif