#ifndef SML3__HASH_H
#define SML3__HASH_H

/* ++++++++++++++++++++
 * +++ Beschreibung +++
 * ++++++++++++++++++++

  Ein Hash ist eine Anordnung von Hash-Elementen (SML3_hashelem).
  Jedes Hash-Element enthaelt einen Key, durch den es eindeutig identifiziert
  wird, und einen Value, der ein beliebiger Wert sein kann (String, Struct,
  Integer, ...).

  Ein Hash ist wie ein Verzeichnis aufgebaut, es kann Unter-Hashs enthalten.
  Diese Unter-Hashs sind in besonderen Hash-Elementen enthalten.
  Auf diese Weise koennen quasi-mehrdimensionale Keys verwendet werden.
  Derselbe Key kann sowohl fuer ein normales Hash-Element als auch fuer
  ein Hash-Element mit Unter-Hash verwendet werden.

  Der Key darf keine Pointer enthalten, die auf extern allozierte Bereiche
  verweisen, sie wuerden nicht freigegeben.
  Der Value darf Pointer fuer extern allozierte Bereiche enthalten,
  dazu muss aber fuer die korrekte Freigabe bei SML3_hashnew() eine
  Deallozierungsfunktion uebergeben werden.
  Genauso muss fuer SML3_hashcopy() eventuell eine Kopierfunktion
  uebergeben werden.

  Zuerst wird ein Hash mit SML3_hashnew() initialisiert;
  Parameter koennen gesetzt werden mit SML3_hashresizeparam().

  Die Deallozierungsfunktion muss bei Funktions-Adress-Aenderung (z.B. innerhalb
  einer neugeladenen Bibliothek) mit SML3_hash_reload_destfk() aktualisiert werden.

  Hash-Elemente erhalten, setzen, loeschen:
   - SML3_hashget()                 Hash-Element erhalten
   - SML3_hashgetarray()            Hash-Element via Key-Feld erhalten
   - SML3_hashset()                 Hash-Element einfuegen, falls fehlt
   - SML3_hashsetarray()            Hash-Element via Key-Feld einfuegen, falls fehlt
   - SML3_hashdel()                 Hash-Element entfernen
   - SML3_hashlist()                Hash-Elemente (Typ=Normal) sukzessive auflisten
  Werte eines Hash-Elements erhalten, setzen, testen:
   - SML3_hashelem_keyget()         Pointer auf Key erhalten
   - SML3_hashelem_keypath()        sukzessive Pointer auf Key im Key-Pfad erhalten
   - SML3_hashelem_keyarray()       gesamtes Key-Feld erhalten
   - SML3_hashelem_valget()         Pointer auf Wert erhalten
   - SML3_hashelem_valset()         Wert setzen
   - SML3_hashelem_valswap()        Wert vertauschen
   - SML3_hashelem_hashget()        Hash des Hash-Elements erhalten
   - SML3_hashelem_typ_is_hash()    testen, ob Hash-Element vom Typ=Unter-Hash ist
   - SML3_hashelem_typ_is_normal()  testen, ob Hash-Element vom Typ=Normal ist
  Aktionen mit Hashs:
   - SML3_hashclear()               Hash leeren
   - SML3_hashcopy()                Hash in ein anderes Hash kopieren
   - SML3_hashswap()                zwei Hashs inhaltlich vertauschen
  Aktionen mit Unter-Hashs:
   - SML3_hashsubget()              Unter-Hash erhalten
   - SML3_hashsubset()              Unter-Hash einfuegen, falls fehlt
   - SML3_hashsubdel()              Hash-Element aus Unter-Hash entfernen
   - SML3_hashsublist()             direkte Unter-Hashs sukzessive auflisten
   - SML3_hashparentelem()          Hash-Element des Ober-Hashs erhalten,
                                      in dem uebergebenes Hash sich befindet
                                      (damit kann dieses gesamte (Unter-)Hash
                                       durch SML3_hashdel() geloescht werden)
   - SML3_hashsubhash()             Gegenteil zu SML3_hashparentelem():
                                      Unter-Hash des Hash-Elements (Typ=Unter-Hash)
                                      erhalten
   - SML3_hashtophash()             oberstes Hash zurueckgeben
  Sortierung von Hashs:
   - SML3_hashsort()                sortiert Hash mittels qsort() in interne verkettete Liste
   - SML3_hashsort_first()          gibt erstes Hash-Element eines sortierten Hashs zurueck
   - SML3_hashsort_last()           gibt letztes Hash-Element eines sortierten Hashs zurueck
   - SML3_hashsort_next()           gibt naechstes Hash-Element eines sortierten Hashs zurueck
   - SML3_hashsort_prev()           gibt voriges Hash-Element eines sortierten Hashs zurueck
  Mit SML3_hashfree() wird das Hash freigegeben
+++ */


/* ++++++++++++++++
 * +++ Beispiel +++
 * ++++++++++++++++

  - Beispiel: Dateien im aktuellen Verzeichnis mit Unterverzeichnissen in Hash setzen

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <unistd.h>
    #include <errno.h>
    #include <dirent.h>
    #include <sammel3.h>

    #ifndef S_ISDIR
    # define S_ISDIR(m)  (((m) & S_IFMT) == S_IFDIR)
    #endif

    // rekursiv gemaess Verzeichnisinhalt Hash aufbauen
    static int
    makehash(const char *dirname, struct SML3_hash *hs1)
    {
      DIR *dirp;
      struct dirent *direntp;
      struct stat sbuf;
      char pfad[2048];

      if ((dirp = opendir(dirname)) == NULL) {
        SML3_fehlernew(errno, "opendir %s: %s", dirname, strerror(errno));
        return -1;
      }
      while ((direntp = readdir(dirp)) != NULL) {
        if (strcmp(direntp->d_name ,".") == 0 || strcmp(direntp->d_name, "..") == 0) { continue; }

        snprintf(pfad ,sizeof(pfad) ,"%s/%s", dirname, direntp->d_name);
        if (lstat(pfad, &sbuf) < 0) {
          closedir(dirp);
          SML3_fehlernew(errno, "lstat %s: %s", pfad, strerror(errno));
          return -1;
        }

        if (S_ISDIR(sbuf.st_mode)) {  // Unter-Verzeichnis: Unter-Hash anlegen
          struct SML3_hash *hsub;
          // Dateiname als Key des Unter-Hashs
          hsub = SML3_hashsubset(hs1, direntp->d_name, strlen(direntp->d_name) + 1);
          // rekursiv aufrufen mit Unter-Hash
          if (makehash(pfad, hsub) < 0) { closedir(dirp); return -1; }

        } else {  // Datei: normalen Hash-Eintrag anlegen
          struct SML3_hashelem *he1;
          unsigned int bytes;
          // Dateiname als Key
          he1 = SML3_hashset(hs1, direntp->d_name, strlen(direntp->d_name) + 1);
          // Dateigroesse als Wert
          bytes = sbuf.st_size;
          SML3_hashelem_valset(he1, &bytes, sizeof(unsigned int));
        }
      }
      closedir(dirp);
      return 0;
    }

    int
    main(void)
    {
    #undef KEY_FELD  // define: SML3_hashelem_keyarray(), undef: SML3_hashelem_keypath()
      struct SML3_hash *hs1 = SML3_hashnew(NULL, 0);
      struct SML3_hashelem *he1;
    #ifdef KEY_FELD
      struct SML3_hash_keyarray karry;
      int i1;
    #else
      struct SML3_hash *hsptr;
      const void *cptr;
    #endif

      // Hash als Abbild des aktuellen Verzeichnisses aufbauen
      if (makehash(".", hs1) < 0) { fprintf(stderr, "%s\n", SML3_fehlermsg()); exit(1); }

    #ifdef KEY_FELD
      // Key-Feld vor Erstbenutzung ausnullen
      memset(&karry, 0, sizeof(karry));
    #endif

      // Ausgabe der normalen Hash-Werte (=Dateien)
      printf("\nHash-Typ = Normal:\n");
      for (he1 = SML3_hashlist(hs1, NULL, 1); he1 != NULL; he1 = SML3_hashlist(hs1, he1, 1)) {
        // Key als Pfad ausgeben
    #ifdef KEY_FELD
        SML3_hashelem_keyarray(NULL, he1, &karry);
        for (i1 = 0; i1 < karry.anz; i1++) {
          printf("%s%s", (const char *)karry.e[i1].keyptr, i1 == karry.anz - 1 ? "" : "/");
        }
    #else
        hsptr = SML3_hashtophash(hs1);  // absolut. Falls relativ:  hsptr = hs1;
        while (SML3_hashelem_keypath(&hsptr, he1, &cptr, NULL)) {
          printf("%s%s", (const char *)cptr, hsptr == NULL ? "" : "/");
        }
    #endif
        // Value (=Dateigroesse) ausgeben
        printf(" = %u\n", *((unsigned int *)SML3_hashelem_valget(he1, NULL)));
      }

      // Ausgabe der Unter-Hashs
      printf("\nHash-Typ = Unter-Hash:\n");
      for (he1 = SML3_hashsublist(hs1, NULL); he1 != NULL; he1 = SML3_hashsublist(hs1, he1)) {
        // Key ausgeben, hat immer nur eine Dimension
        printf("%s/\n", (char *)SML3_hashelem_keyget(he1, NULL));
      }

      // Hash freigeben
      SML3_hashfree(&hs1);
    #ifdef KEY_FELD
      // Key-Feld freigeben
      SML3_hashelem_keyarray(NULL, NULL, &karry);
    #endif
      exit(0);
    }


  - Beispiel: Hash numerisch sortieren nach Keys

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/types.h>
    #include <unistd.h>
    #include <errno.h>
    #include <sammel3.h>

    // Vergleichsfunktion fuer qsort(), sortiert nach Keys numerisch aufwaerts
    static int
    compar(const void *v1, const void *v2)
    {
      struct SML3_hashelem *he1 = *(struct SML3_hashelem **)v1;
      struct SML3_hashelem *he2 = *(struct SML3_hashelem **)v2;
      int i1 = atoi((char *)SML3_hashelem_keyget(he1, NULL));
      int i2 = atoi((char *)SML3_hashelem_keyget(he2, NULL));
      if (i1 < i2) { return -1; }
      if (i1 > i2) { return 1; }
      return 0;
    }


    int
    main(void)
    {
      struct SML3_hash *hs1;
      struct SML3_hashelem *he1;
      int zahl;
      char buf[32];

      hs1 = SML3_hashnew(NULL, 0);

      // Hash fuellen mit Keys = Zahlen von 1 bis 30
      for (zahl = 1; zahl <= 30; zahl++) {
        snprintf(buf, sizeof(buf), "%d", zahl);
        SML3_hashset(hs1, buf, strlen(buf) + 1);
      }

      // Hash-Ausgabe
      printf("Hash:\n  ");
      for (he1 = SML3_hashlist(hs1, NULL, 0); he1 != NULL; he1 = SML3_hashlist(hs1, he1, 0)) {
        printf(" %s", (const char *)SML3_hashelem_keyget(he1, NULL));
      }
      printf("\n");

      // Hash sortieren
      SML3_hashsort(hs1, compar, 0);

      // sortierte Hash-Ausgabe vorwaerts
      printf("Hash aufwaerts sortiert:\n  ");
      for (he1 = SML3_hashsort_first(hs1); he1 != NULL; he1 = SML3_hashsort_next(he1)) {
        printf(" %s", (const char *)SML3_hashelem_keyget(he1, NULL));
      }
      printf("\n");

      // sortierte Hash-Ausgabe rueckwaerts
      printf("Hash abwaerts sortiert:\n  ");
      for (he1 = SML3_hashsort_last(hs1); he1 != NULL; he1 = SML3_hashsort_prev(he1)) {
        printf(" %s", (const char *)SML3_hashelem_keyget(he1, NULL));
      }
      printf("\n");

      // Hash freigeben
      SML3_hashfree(&hs1);

      exit(0);
    }
+++ */


/* ++++++++++++++++++ */
/* +++ Funktionen +++ */
/* ++++++++++++++++++ */

#include <sys/types.h>

struct SML3_hashelem {         /* Hash-Element */
  struct SML3_hashelem *prev;    /* als verkettete Liste: voriges Hash-Element (nur fuer Sortierung) */
  struct SML3_hashelem *next;    /* als verkettete Liste: naechstes Hash-Element (nur fuer Sortierung) */
  void *home;                    /* Adresse des Hashs */
  unsigned int listepos;         /* Nummer der Hash-Element-Liste */
  char typ;                      /* Value-Typ: HASHTYP_* */
  struct {                       /* Key */
    void *inh;                     /* Inhalt */
    size_t size;                   /* Groesse in Bytes */
  } key;
  struct {                       /* Value (siehe Value-Typ) */
    void *inh;                     /* Inhalt */
    size_t size;                   /* Groesse in Bytes */
  } val;
};

struct SML3_hash {            /* Hash */
  void *home;                   /* Adresse des Hash-Elements des Oberhashs oder NULL */
  void (*destfk)(void *);       /* Deallozierungsfunktion fuer Hash-Elemente oder NULL */
  struct {                      /* dynamisches Hash-Wachstum (anzliste) */
    int sperr;                    /* ob rekursiv gesperrt */
    unsigned short minteil;       /* Teiler-Prozent, den Wachstum erreichen soll (anzliste = anzelem * 100 / hg.minteil) */
    unsigned short maxteil;       /* Teiler, wann Wachstum aktiv wird (sobald: anzelem >= anzliste * hg.maxteil) */
  } hg;
  unsigned int anzliste;        /* Anzahl Hash-Element-Listen (Primzahl) */
  unsigned int anzelem;         /* Gesamtanzahl Hash-Elemente */
  struct {                      /* Hash-Element-Listen */
    int max;                      /* Anzahl Elemente der Hash-Element-Liste alloziert */
    int anz;                      /* Anzahl Elemente der Hash-Element-Liste benutzt */
    struct SML3_hashelem **elem;  /* Hash-Element-Liste */
  } *liste;
};

struct SML3_hash_keyarray {  /* Key-Feld */
  int ishash;                  /* Typ: 0 = normal, 1 = Unter-Hash */
  int anz;                     /* Anzahl Keys */
  struct {                     /* Keys */
    const void *keyptr;          /* Key-Pointer */
    size_t keysize;              /* Key-Laenge */
  } *e;
};


/** SML3_hashnew [thread-sicher]:
 * gibt neues Hash zurueck
 * 1.Arg: Deallozierungsfunktion fuer Values der Hash-Elemente
 *          1.Parameter: Pointer auf Value im Hash-Element
 *        oder NULL = keine
 * 2.Arg: Initial-Element-Listenanzahl (wird auf naechste Primzahl aufgerundet)
 *        oder 0
 * Rueckgabe: Hash
 */
extern struct SML3_hash * SML3_hashnew(void (*)(void *), unsigned int);


/** SML3_hashresizeparam [reentrant]:
 * setzt Resize-Parameter,
 * sobald der maximale Teiler (Elementanzahl / Listenanzahl)
 * erreicht ist, wird das Hash auf den optimalen Teiler vergroessert.
 * Sind beide auf 0 gesetzt, ist Hash-Vergroesserung deaktiviert.
 * 1.Arg: Hash
 * 2.Arg: optimaler Teiler-Prozent (Standard = 200  ==>  Teiler = 2)
 * 3.Arg: maximaler Teiler (Standard = 50)
 */
extern void SML3_hashresizeparam(struct SML3_hash *, unsigned int, unsigned int);


/** SML3_hash_reload_destfk [reentrant]:
 * zum Erneuern der Deallozierungsfunktion, die bei SML3_hashnew() uebergeben wurde,
 * ist nicht zum Veraendern, sondern zum Aktualisieren gedacht (bei Funktions-Adress-Aenderung)
 * 1.Arg: Hash
 * 2.Arg: Deallozierungsfunktion (siehe SML3_hashnew())
 */
extern void SML3_hash_reload_destfk(struct SML3_hash *, void (*)(void *));


/** SML3_hashfree [ohne Deallozierungsfunktion: thread-sicher]:
 * gibt Hash frei
 * 1.Arg: Adresse auf Hash
 */
extern void SML3_hashfree(struct SML3_hash **);


/** SML3_hashclear [thread-sicher]:
 * leert Hash
 * 1.Arg: Hash
 */
extern void SML3_hashclear(struct SML3_hash *);


/** SML3_hashcopy [ohne Kopierfunktion: thread-sicher]:
 * gibt kopiertes Hash zurueck
 * 1.Arg: Quell-Hash
 * 2.Arg: Kopierfunktion fuer Values der Hash-Elemente (analog Deallozierungsfunktion)
 *          1.Parameter: Pointer auf Value im Ziel-Hash-Element
 *        oder NULL = keine
 * Rueckgabe: kopiertes Hash
 */
extern struct SML3_hash * SML3_hashcopy(struct SML3_hash *, void (*)(void *));


/** SML3_hashswap [thread-sicher]:
 * vertauscht Inhalte beider Hashs
 * 1.+2.Arg: Hashs (beide muessen zumindest initialisiert sein)
 */
extern void SML3_hashswap(struct SML3_hash *, struct SML3_hash *);


/** SML3_hashget [thread-sicher]:
 * gibt vorhandenes Hash-Element zurueck
 * 1.Arg: Hash
 * 2.+3.Arg: Key-Pointer und Key-Laenge
 * Rueckgabe: Hash-Element oder NULL = nicht gefunden
 */
extern struct SML3_hashelem * SML3_hashget(struct SML3_hash *, const void *, size_t);


/** SML3_hashset [thread-sicher]:
 * gibt vorhandenes oder neu erzeugtes Hash-Element zurueck
 * 1.Arg: Hash
 * 2.+3.Arg: Key-Pointer und Key-Laenge
 * Rueckgabe: Hash-Element
 *
 * Es ist moeglich, als 2.Arg statt eines Pointers eine Zahl zu uebergeben,
 * dazu muss das 3.Arg = 0 gesetzt werden; dies dient nur dazu, die Allokation
 * fuer den Key zu sparen.
 * Beispiel:
 *   struct SML3_hash *h1 = SML3_hashnew(NULL, 0);
 *   struct SML3_hashelem *he1;
 *   int i1 = 1;
 *   he1 = SML3_hashset(h1, (const void *)(size_t)i1, 0);
 *   he1 = SML3_hashget(h1, (const void *)(size_t)i1, 0);
 *   i1 = (int)(size_t)SML3_hashelem_keyget(he1, NULL);
 *   printf("Key=%d\n", i1);
 *   SML3_hashfree(&h1);
 */
extern struct SML3_hashelem * SML3_hashset(struct SML3_hash *, const void *, size_t);


/** SML3_hashdel [ohne Deallozierungsfunktion: thread-sicher]:
 * loescht uebergebenes Hash-Element
 * 1.Arg: Hash-Element
 * Rueckgabe: voriges Hash-Element oder NULL = kein voriges vorhanden
 * Achtung!
 * Wird ein Sub-Hash durchlaufen und dessen erstes Element geloescht,
 * ist die Rueckgabe ein voriges Element ausserhalb des Sub-Hashs statt NULL.
 * Dafuer wird SML3_hashsubdel() empfohlen.
 */
extern struct SML3_hashelem * SML3_hashdel(struct SML3_hashelem *);


/** SML3_hashlist [thread-sicher]:
 * gibt naechstes Hash-Element (Typ=Normal) zurueck
 * 1.Arg: Hash
 * 2.Arg: aktuelles Hash-Element oder NULL = ab Beginn
 * 3.Arg: ob Unter-Hashs auch durchsuchen (1 = ja, 0 = nein)
 * Rueckgabe: naechstes Hash-Element oder NULL = Ende
 */
extern struct SML3_hashelem * SML3_hashlist(struct SML3_hash *, struct SML3_hashelem *, int);


/** SML3_hashsubget [thread-sicher]:
 * gibt vorhandenes Unter-Hash zurueck
 * 1.Arg: Hash, in dem gesucht wird
 * 2.+3.Arg: Key-Pointer und Key-Laenge
 * Rueckgabe: Unter-Hash oder NULL = nicht gefunden
 */
extern struct SML3_hash * SML3_hashsubget(struct SML3_hash *, const void *, size_t);


/** SML3_hashsubset [thread-sicher]:
 * gibt vorhandenes oder neu erzeugtes Unter-Hash zurueck
 * 1.Arg: Hash, in dem gesucht wird
 * 2.+3.Arg: Key-Pointer und Key-Laenge
 * Rueckgabe: Unter-Hash
 *
 * Es ist moeglich, als 2.Arg statt eines Pointers eine Zahl zu uebergeben,
 * dazu muss das 3.Arg = 0 gesetzt werden; dies dient nur dazu, die Allokation
 * fuer den Key zu sparen.
 * Beispiel:
 *   struct SML3_hash *h1 = SML3_hashnew(NULL, 0);
 *   struct SML3_hash *hs1;
 *   int i1 = 1;
 *   hs1 = SML3_hashsubset(h1, (const void *)(size_t)i1, 0);
 *   hs1 = SML3_hashsubget(h1, (const void *)(size_t)i1, 0);
 *   SML3_hashdest(&h1);
 */
extern struct SML3_hash * SML3_hashsubset(struct SML3_hash *, const void *, size_t);


/* SML3_hashsubdel [ohne Deallozierungsfunktion: thread-sicher]:
 * loescht uebergebenes Hash-Element unterhalb (Sub-)Hash
 * 1.Arg: (Sub-)Hash, oder NULL = oberstes Hash (entspricht SML3_hashdel())
 * 2.Arg: Hash-Element
 * Rueckgabe: voriges Hash-Element (unterhalb (Sub-)Hash) oder NULL = kein voriges vorhanden
 */
extern struct SML3_hashelem * SML3_hashsubdel(struct SML3_hash *, struct SML3_hashelem *);


/** SML3_hashsublist [thread-sicher]:
 * gibt naechstes Hash-Element fuer ein direktes Unter-Hash des 1.Arg zurueck
 * 1.Arg: Hash
 * 2.Arg: aktuelles Hash-Element oder NULL = ab Beginn
 * Rueckgabe: naechstes Unter-Hash-Element oder NULL = Ende
 */
extern struct SML3_hashelem * SML3_hashsublist(struct SML3_hash *, struct SML3_hashelem *);


/** SML3_hashelem_valget [reentrant]:
 * gibt Pointer auf Value des Hash-Elements zurueck
 * 1.Arg: Hash-Element
 * 2.Arg: Adresse fuer Rueckgabe Anzahl Bytes im Value
 *        oder NULL
 * Rueckgabe: Pointer auf Value oder NULL = nicht vorhanden
 *            bzw. Zahl (siehe SML3_hashelem_valset())
 */
extern void * SML3_hashelem_valget(struct SML3_hashelem *, size_t *);


/** SML3_hashelem_valset [ohne Deallozierungsfunktion: thread-sicher]:
 * setzt Value des Hash-Elements,
 * ein eventuell vorhandener Value wird freigegeben
 * 1.Arg: Hash-Element
 * 2.Arg: Value (oder NULL)
 * 3.Arg: Anzahl Bytes im Value
 *
 * Es ist moeglich, als 2.Arg statt eines Pointers eine Zahl zu uebergeben,
 * dazu muss das 3.Arg = 0 gesetzt werden; dies dient nur dazu, die Allokation
 * fuer den Value zu sparen.
 * Beispiel:
 *   struct SML3_hash *h1 = SML3_hashnew(NULL, 0);
 *   SML3_hashelem *he1 = SML3_hashset(h1, "Key", 4);
 *   #if 1  // mit Pointer
 *     int *ret1;
 *     { int i1 = 1;
 *       SML3_hashelem_valset(he1, &i1, sizeof(int));  // alloziert Value, setzt als Wert 1
 *     }
 *     ret1 = (int *)SML3_hashelem_valget(he1, NULL);
 *     printf("Value=%d\n", *ret1);
 *   #else  // mit Zahl
 *     int ret1;
 *     { int i1 = 1;
 *       SML3_hashelem_valset(he1, (const void *)(size_t)i1, 0);  // setzt Value als Wert 1 ohne Allokation
 *     }
 *     ret1 = (int)(size_t)SML3_hashelem_valget(he1, NULL);
 *     printf("Value=%d\n", ret1);
 *   #endif
 *   SML3_hashdest(&h1);
 */
extern void SML3_hashelem_valset(struct SML3_hashelem *, const void *, size_t);


/** SML3_hashelem_valswap [reentrant]:
 * vertauscht Value des Hash-Elements mit uebergebenem Value
 * 1.Arg: Hash-Element
 * 2.Arg: Adresse auf Daten
 *        Uebergabe: zu setzende Daten, muessen alloziert sein mit Size=3.Arg
 *        Rueckgabe: alte Daten, sind alloziert mit Size=3.Arg
 * 3.Arg: Adresse auf Size der Daten
 *        Uebergabe: allozierte Size der zu setzenden Daten
 *        Rueckgabe: allozierte Size der alten Daten
 */
extern void SML3_hashelem_valswap(struct SML3_hashelem *, void **, size_t *);


/** SML3_hashelem_keyget [reentrant]:
 * gibt Pointer auf Key des Hash-Elements zurueck
 * 1.Arg: Hash-Element
 * 2.Arg: Adresse fuer Rueckgabe Anzahl Bytes im Key
 *        oder NULL
 * Rueckgabe: Pointer auf Key (oder NULL, falls 1.Arg = NULL)
 */
extern const void * SML3_hashelem_keyget(struct SML3_hashelem *, size_t *);


/** SML3_hashelem_keypath [reentrant]:
 * gibt sukzessive Pointer auf Key-Pfad des Hash-Elements zurueck
 * 1.Arg: Adresse auf Start-Hash
 *        (wird veraendert! Ist NULL nach letztem Aufruf mit OK-Rueckgabe)
 * 2.Arg: Hash-Element
 * 3.Arg: Adresse fuer Rueckgabe Key
 * 4.Arg: Adresse fuer Rueckgabe Anzahl Bytes im Key oder NULL
 * Rueckgabe: 1 = OK oder 0 = Ende
 */
extern int SML3_hashelem_keypath(struct SML3_hash **, struct SML3_hashelem *, const void **, size_t *);


/** SML3_hashelem_hashget [reentrant]:
 * gibt Pointer auf Hash des Hash-Elements zurueck
 * 1.Arg: Hash-Element
 * Rueckgabe: Pointer auf Hash (oder NULL, falls 1.Arg = NULL)
 */
extern struct SML3_hash * SML3_hashelem_hashget(struct SML3_hashelem *);


/** SML3_hashelem_typ_is_hash [reentrant]:
 * testet, ob Hash-Element vom Typ=Unter-Hash ist
 * 1.Arg: Hash-Element
 * Rueckgabe: 1 = ja, 0 = nein
 * Das Unter-Hash kann mit SML3_hashsubhash() erhalten werden
 */
extern int SML3_hashelem_typ_is_hash(struct SML3_hashelem *);


/** SML3_hashelem_typ_is_normal [reentrant]:
 * testet, ob Hash-Element vom Typ=Normal ist
 * 1.Arg: Hash-Element
 * Rueckgabe: 1 = ja, 0 = nein
 */
extern int SML3_hashelem_typ_is_normal(struct SML3_hashelem *);


/** SML3_hashparentelem [reentrant]:
 * gibt Pointer auf Ober-Hash-Element des Hashs zurueck
 * 1.Arg: Hash
 * Rueckgabe: Pointer auf Ober-Hash-Element
 *            oder NULL = kein Ober-Hash-Element vorhanden
 */
extern struct SML3_hashelem * SML3_hashparentelem(struct SML3_hash *);


/** SML3_hashsubhash [reentrant]:
 * gibt Unter-Hash des Hash-Elements (Typ=Unter-Hash) zurueck
 * 1.Arg: Hash-Element
 * Rueckgabe: Pointer auf Unter-Hash
 *            oder NULL = kein Unter-Hash vorhanden
 *                        (Hash-Element hat nicht Typ=Unter-Hash)
 */
extern struct SML3_hash * SML3_hashsubhash(struct SML3_hashelem *);


/** SML3_hashtophash [reentrant]:
 * gibt oberstes Hash zurueck
 * 1.Arg: Hash
 * Rueckgabe: oberstes Hash (oder NULL, falls 1.Arg = NULL)
 */
extern struct SML3_hash * SML3_hashtophash(struct SML3_hash *);


/** SML3_hashelem_keyarray [thread-sicher]:
 * gibt Key-Feld des Hash-Elements zurueck
 * 1.Arg: Hash, ab dem Key-Feld zurueckgegeben werden soll, oder NULL = alles
 * 2.Arg: Hash-Element
 * 3.Arg: fuer Rueckgabe Key-Feld (muss entweder ausgenullt oder darf schon benutzt worden sein)
 *        nach Benutzung muss Element "e" freigegeben werden, z.B. mit:
 *          SML3_hashelem_keyarray(NULL, NULL, 3.Arg);
 */
extern void SML3_hashelem_keyarray(struct SML3_hash *, struct SML3_hashelem *, struct SML3_hash_keyarray *);


/** SML3_hashgetarray [thread-sicher]:
 * gibt vorhandenes Hash-Element (vom Typ Normal oder Unter-Hash) via Key-Feld zurueck
 * 1.Arg: Hash
 * 2.Arg: Key-Feld
 * Rueckgabe: Hash-Element oder NULL = nicht gefunden
 */
extern struct SML3_hashelem * SML3_hashgetarray(struct SML3_hash *, struct SML3_hash_keyarray *);


/** SML3_hashsetarray [thread-sicher]:
 * gibt vorhandenes oder neu erzeugtes Hash-Element (vom Typ Normal oder Unter-Hash) via Key-Feld zurueck
 * 1.Arg: Hash
 * 2.Arg: Key-Feld
 * Rueckgabe: Hash-Element (oder NULL, wenn kein Key im 2.Arg vorhanden ist)
 *
 * Beispiel, wenn Hash hs2 in Hash hs1 eingemischt werden soll:
 *   struct SML3_hashelem *he1;
 *   struct SML3_hash_keyarray karry;
 *   memset(&karry, 0, sizeof(karry));  // ausnullen
 *   // jedes Element von hs2 in hs1 einkopieren
 *   for (he1 = SML3_hashlist(hs2, NULL, 1); he1 != NULL; he1 = SML3_hashlist(hs2, he1, 1)) {
 *     SML3_hashelem_keyarray(NULL, he1, &karry);  // Key-Feld von he1 erhalten
 *     SML3_hashsetarray(hs1, &karry);  // in hs1 mit gleichem Key-Feld anlegen
 *     // nun noch den Value kopieren ...
 *   }
 */
extern struct SML3_hashelem * SML3_hashsetarray(struct SML3_hash *, struct SML3_hash_keyarray *);


/** SML3_hashsort [nicht thread-sicher wegen qsort()]:
 * sortiert Hash mit qsort(), d.h. setzt Elemente "prev" und "next" in jedem Hashelement
 * 1.Arg: Hash
 * 2.Arg: Vergleichsfunktion fuer qsort (die Parameter sind: struct SML3_hashelem **)
 * 3.Arg: ob Unter-Hashs auch durchsuchen (1 = ja, 0 = nein)
 */
extern void SML3_hashsort(struct SML3_hash *, int (*)(const void *, const void *), int);


/** SML3_hashsort_first [thread-sicher]:
 * gibt erstes Element eines sortierten Hashs zurueck
 * 1.Arg: Hash
 * Rueckgabe: erstes Element (oder NULL)
 */
extern struct SML3_hashelem * SML3_hashsort_first(struct SML3_hash *);


/** SML3_hashsort_last [thread-sicher]:
 * gibt letztes Element eines sortierten Hashs zurueck
 * 1.Arg: Hash
 * Rueckgabe: letztes Element (oder NULL)
 */
extern struct SML3_hashelem * SML3_hashsort_last(struct SML3_hash *);


/** SML3_hashsort_next [reentrant]:
 * gibt naechstes Element eines sortierten Hashs zurueck
 * 1.Arg: Hash-Element
 * Rueckgabe: naechstes Element oder NULL = Ende
 */
extern struct SML3_hashelem * SML3_hashsort_next(struct SML3_hashelem *);


/** SML3_hashsort_prev [reentrant]:
 * gibt voriges Element eines sortierten Hashs zurueck
 * 1.Arg: Hash-Element
 * Rueckgabe: voriges Element oder NULL = Ende
 */
extern struct SML3_hashelem * SML3_hashsort_prev(struct SML3_hashelem *);


/** SML3_hashdump [ohne Ausgabefunktionen: thread-sicher]:
 * Dump nach stdout
 * 1.Arg: Hash
 * 2.Arg: Ausgabefunktion fuer Key
 *          (Ausgabe sollte kurz in einer Zeile sein)
 *          1.Parameter: Pointer auf Key
 *          2.Parameter: Anzahl Bytes im Key
 *        oder NULL = keine (=> Adressausgabe)
 * 3.Arg: Ausgabefunktion fuer Value
 *          (Ausgabe sollte kurz in einer Zeile sein)
 *          1.Parameter: Pointer auf Value
 *          2.Parameter: Anzahl Bytes im Value
 *        oder NULL = keine (=> Adressausgabe)
 */
extern void SML3_hashdump(struct SML3_hash *, void (*)(const void *, size_t), void (*)(const void *, size_t));

#endif /* SML3__HASH_H */
