/* sml3_hash.c: Hash-Funktionen */

/* Copyright 2012-2017 Kurt Nienhaus
 *
 * This file is part of libsammel3.
 * libsammel3 is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 * libsammel3 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 General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with libsammel3.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include "config.h"
#include "sml3_fehler.h"
#include "sml3_util.h"
#include "sml3_hash.h"
 
#define HASH_INITIALIZER  { NULL, NULL, { 0, 200, 50 }, 0, 0, NULL }

struct SML3_hash * SML3_hashnew(void (*)(void *), unsigned int);
void SML3_hashresizeparam(struct SML3_hash *, unsigned int, unsigned int);
void SML3_hash_reload_destfk(struct SML3_hash *, void (*)(void *));
void SML3_hashfree(struct SML3_hash **);
void SML3_hashclear(struct SML3_hash *);
struct SML3_hash * SML3_hashcopy(struct SML3_hash *, void (*)(void *));
void SML3_hashswap(struct SML3_hash *, struct SML3_hash *);
struct SML3_hashelem * SML3_hashget(struct SML3_hash *, const void *, size_t);
struct SML3_hashelem * SML3_hashset(struct SML3_hash *, const void *, size_t);
struct SML3_hashelem * SML3_hashdel(struct SML3_hashelem *);
struct SML3_hashelem * SML3_hashlist(struct SML3_hash *, struct SML3_hashelem *, int);
struct SML3_hash * SML3_hashsubget(struct SML3_hash *, const void *, size_t);
struct SML3_hash * SML3_hashsubset(struct SML3_hash *, const void *, size_t);
struct SML3_hashelem * SML3_hashsubdel(struct SML3_hash *, struct SML3_hashelem *);
struct SML3_hashelem * SML3_hashsublist(struct SML3_hash *, struct SML3_hashelem *);
void * SML3_hashelem_valget(struct SML3_hashelem *, size_t *);
void SML3_hashelem_valset(struct SML3_hashelem *, const void *, size_t);
void SML3_hashelem_valswap(struct SML3_hashelem *, void **, size_t *);
const void * SML3_hashelem_keyget(struct SML3_hashelem *, size_t *);
int SML3_hashelem_keypath(struct SML3_hash **, struct SML3_hashelem *, const void **, size_t *);
struct SML3_hash * SML3_hashelem_hashget(struct SML3_hashelem *);
int SML3_hashelem_typ_is_hash(struct SML3_hashelem *);
int SML3_hashelem_typ_is_normal(struct SML3_hashelem *);
struct SML3_hashelem * SML3_hashparentelem(struct SML3_hash *);
struct SML3_hash * SML3_hashsubhash(struct SML3_hashelem *);
struct SML3_hash * SML3_hashtophash(struct SML3_hash *);
void SML3_hashelem_keyarray(struct SML3_hash *, struct SML3_hashelem *, struct SML3_hash_keyarray *);
struct SML3_hashelem * SML3_hashgetarray(struct SML3_hash *, struct SML3_hash_keyarray *);
struct SML3_hashelem * SML3_hashsetarray(struct SML3_hash *, struct SML3_hash_keyarray *);
void SML3_hashsort(struct SML3_hash *, int (*)(const void *, const void *), int);
struct SML3_hashelem * SML3_hashsort_first(struct SML3_hash *);
struct SML3_hashelem * SML3_hashsort_last(struct SML3_hash *);
struct SML3_hashelem * SML3_hashsort_next(struct SML3_hashelem *);
struct SML3_hashelem * SML3_hashsort_prev(struct SML3_hashelem *);
void SML3_hashdump(struct SML3_hash *, void (*)(const void *, size_t), void (*)(const void *, size_t));

enum { HASHTYP_NORMAL = '-', HASHTYP_SUBHASH = 'd', HASHTYP_INVALID = ' ' };

static const unsigned int maxliste = 99999989U;  /* Primzahl: maximale Anzahl Hash-Element-Listen */
static const unsigned int startliste = 101U;  /* Primzahl: Anzahl Hash-Element-Listen bei Erstallokation */

static unsigned int errechne_listepos(const void *, size_t, unsigned int);
static int errechne_elempos(int *, struct SML3_hash *, unsigned int, const void *, size_t, int, int);
static struct SML3_hashelem * erhalte_hashelem(struct SML3_hash *, unsigned int, const void *, size_t, int, int);
static int resize_hash(struct SML3_hash *);
static struct SML3_hashelem * voriges_hashelem(struct SML3_hash *, struct SML3_hash *, unsigned int, int, int, int);
static struct SML3_hashelem * naechstes_hashelem(struct SML3_hash *, struct SML3_hash *, unsigned int, int, int, int, int);
static void dump_hashelem(struct SML3_hashelem *, void (*)(const void *, size_t), void (*)(const void *, size_t));
static void hashdump(int, struct SML3_hash *, void (*)(const void *, size_t), void (*)(const void *, size_t));
static void reload_destfk(struct SML3_hash *, void (*)(void *), void (*)(void *));
static void hashinit(struct SML3_hash *, void (*)(void *), unsigned int);
static void hashdest(struct SML3_hash *);
static void hashcopy(struct SML3_hash *, struct SML3_hash *, void (*)(void *));


/* errechne_listepos [reentrant]:
 * berechnet Listen-Position des Keys mithilfe uebergebener Primzahl
 * 1.+2.Arg: Key-Pointer und Key-Laenge
 * 3.Arg: Anzahl Hash-Element-Listen (Primzahl)
 * Rueckgabe: Listen-Position
 */
static unsigned int
errechne_listepos(const void *keyptr, size_t keysize, unsigned int primz)
{
  size_t keypos;
  unsigned SML3_int64 vl1;

  if (primz == 0) { return 0; }
  if (keyptr == NULL) { keysize = 0; }

  if (keysize == 0) {
    vl1 = (size_t)keyptr % primz;
  } else {
    const char *cptr = (const char *)keyptr;
    vl1 = 0;
    for (keypos = 0; keypos < keysize; keypos++) {
      vl1 = (vl1 * 256 + (const unsigned char)cptr[keypos]) % primz;  /* Horner-Schema */
    }
  }
  return (unsigned int)vl1;
} /* Ende errechne_listepos */


/* errechne_elempos [thread-sicher]:
 * errechnet Element-Position des Hash-Elements des Keys
 * 1.Arg: fuer Rueckgabe Element-Position
 * 2.Arg: Hash
 * 3.Arg: Listen-Position des Keys
 * 4.+5.Arg: Key-Pointer und Key-Laenge
 * 6.Arg: Value-Typ
 * 7.Arg: ob Element-Position anlegen, falls fehlt: 0 = nein, 1 = ja
 * Rueckgabe:  1 = OK: neu angelegt
 *             0 = OK: war schon vorhanden
 *            -1 = nicht gefunden / Fehler
 * SML3-errno-Wert: 0      = nicht gefunden
 *                  EINVAL = Fehler Uebergabeparameter
 */
static int
errechne_elempos(int *ipos, struct SML3_hash *hsptr, unsigned int listepos, const void *keyptr, size_t keysize, int typ, int create)
{
  const int blockalloc = 4;
  int i1;

  if (keyptr == NULL) { keysize = 0; }
  if (ipos == NULL || hsptr == NULL || hsptr->liste == NULL || hsptr->anzliste == 0 || listepos >= hsptr->anzliste) {
    SML3_fehlernew(EINVAL, "%s", SML3_strerror(EINVAL));
    return -1;
  }

  if (hsptr->liste[listepos].max == 0) {  /* keine Liste vorhanden */
    if (!create) { SML3_fehlernew(0, NULL); return -1; }
    hsptr->liste[listepos].elem = SML3_malloc(sizeof(struct SML3_hashelem *) * blockalloc);
    hsptr->liste[listepos].max = blockalloc;
    hsptr->liste[listepos].anz = 1;
    *ipos = 0;

  } else {  /* Key in der Liste suchen */
    int links, rechts;
    *ipos = links = 0; rechts = hsptr->liste[listepos].anz - 1;
    if (keysize == 0) {
      while (links <= rechts) {  /* binaere Suche */
        *ipos = (links + rechts) / 2;
        if (hsptr->liste[listepos].elem[*ipos]->key.size == 0) {
          if ((size_t)hsptr->liste[listepos].elem[*ipos]->key.inh < (size_t)keyptr) {
            i1 = -1;
          } else if ((size_t)hsptr->liste[listepos].elem[*ipos]->key.inh > (size_t)keyptr) {
            i1 = 1;
          } else {
            i1 = (int)(unsigned char)hsptr->liste[listepos].elem[*ipos]->typ - (int)(unsigned char)typ;
            if (i1 == 0) { break; }
          }
        } else {
          i1 = 1;
        }
        if (i1 > 0) { rechts = *ipos - 1; } else { links = *ipos + 1; }
      }
    } else {
      while (links <= rechts) {  /* binaere Suche */
        *ipos = (links + rechts) / 2;
        if (hsptr->liste[listepos].elem[*ipos]->key.size > 0) {
          if (hsptr->liste[listepos].elem[*ipos]->key.size < keysize) {
            i1 = memcmp(hsptr->liste[listepos].elem[*ipos]->key.inh, keyptr, hsptr->liste[listepos].elem[*ipos]->key.size);
            if (i1 == 0) { i1 = -1; }
          } else {
            i1 = memcmp(hsptr->liste[listepos].elem[*ipos]->key.inh, keyptr, keysize);
            if (i1 == 0 && hsptr->liste[listepos].elem[*ipos]->key.size > keysize) { i1 = 1; }
          }
          if (i1 == 0) {
            i1 = (int)(unsigned char)hsptr->liste[listepos].elem[*ipos]->typ - (int)(unsigned char)typ;
            if (i1 == 0) { break; }
          }
        } else {
          i1 = -1;
        }
        if (i1 > 0) { rechts = *ipos - 1; } else { links = *ipos + 1; }
      }
    }
    if (rechts >= links) {  /* gefunden */
      return 0;
    }

    /* nicht gefunden */
    if (!create) { SML3_fehlernew(0, NULL); return -1; }
    /* anlegen */
    if (links > *ipos) { (*ipos)++; }
    hsptr->liste[listepos].anz++;
    if (hsptr->liste[listepos].anz > hsptr->liste[listepos].max) {  /* Liste vergroessern */
      hsptr->liste[listepos].elem =
        SML3_realloc(hsptr->liste[listepos].elem, sizeof(struct SML3_hashelem *) * (hsptr->liste[listepos].anz + blockalloc));
      hsptr->liste[listepos].max = hsptr->liste[listepos].anz + blockalloc;
    }
    i1 = hsptr->liste[listepos].anz - 1 - *ipos;
    if (i1 > 0) {
      memmove(&hsptr->liste[listepos].elem[*ipos + 1], &hsptr->liste[listepos].elem[*ipos], sizeof(struct SML3_hashelem *) * i1);
    }
  }

  return 1;
} /* Ende errechne_elempos */


/* erhalte_hashelem [thread-sicher]:
 * gibt Hash-Element des Keys zurueck
 * 1.Arg: Hash
 * 2.Arg: Listen-Position des Keys
 * 3.+4.Arg: Key-Pointer und Key-Laenge
 * 5.Arg: Value-Typ
 * 6.Arg: ob Hash-Element anlegen, falls fehlt: 0 = nein, 1 = ja
 * Rueckgabe: Hash-Element oder NULL (nicht gefunden / Fehler)
 * SML3-errno-Wert: 0      = nicht gefunden
 *                  EINVAL = Fehler Uebergabeparameter
 */
static struct SML3_hashelem *
erhalte_hashelem(struct SML3_hash *hsptr, unsigned int listepos, const void *keyptr, size_t keysize, int typ, int create)
{
  int ipos, i1;
  struct SML3_hashelem *he1;

  if (keyptr == NULL) { keysize = 0; }
  if (hsptr == NULL || hsptr->liste == NULL || hsptr->anzliste == 0 || listepos >= hsptr->anzliste) {
    SML3_fehlernew(EINVAL, "%s", SML3_strerror(EINVAL));
    return NULL;
  }

  i1 = errechne_elempos(&ipos, hsptr, listepos, keyptr, keysize, typ, create);
  if (i1 < 0) {
    if (SML3_fehlererrno() != 0) { SML3_fehleradd(NULL); }
    return NULL;
  }
  if (i1 == 0) { return hsptr->liste[listepos].elem[ipos]; }

  /* neues Hash-Element erstellen und einfuegen */
  hsptr->liste[listepos].elem[ipos] = SML3_calloc(1, sizeof(struct SML3_hashelem));
  hsptr->liste[listepos].elem[ipos]->prev = NULL;
  hsptr->liste[listepos].elem[ipos]->next = NULL;
  hsptr->liste[listepos].elem[ipos]->home = hsptr;
  hsptr->liste[listepos].elem[ipos]->listepos = listepos;
  hsptr->liste[listepos].elem[ipos]->typ = typ;
  if (keysize > 0) {
    hsptr->liste[listepos].elem[ipos]->key.inh = SML3_malloc(keysize);
    memmove(hsptr->liste[listepos].elem[ipos]->key.inh, keyptr, keysize);
  } else {
    hsptr->liste[listepos].elem[ipos]->key.inh = (void *)keyptr;
  }
  hsptr->liste[listepos].elem[ipos]->key.size = keysize;
  hsptr->liste[listepos].elem[ipos]->val.inh = NULL;
  hsptr->liste[listepos].elem[ipos]->val.size = 0;
  hsptr->anzelem++;
  he1 = hsptr->liste[listepos].elem[ipos];

  if (resize_hash(hsptr) < 0) { SML3_fehleradd(NULL); return NULL; }
  return he1;
} /* Ende erhalte_hashelem */


/* resize_hash [thread-sicher]:
 * passt Hash-Groesse an
 * 1.Arg: Hash
 * Rueckgabe: 0 = OK oder -1 = Fehler 
 * SML3-errno-Wert: EINVAL = Fehler Uebergabeparameter
 */
static int
resize_hash(struct SML3_hash *hsptr)
{
  unsigned int anzliste;
  int anz, ipos, i1;
  struct SML3_hashelem *he1;
  struct SML3_hash hs1 = HASH_INITIALIZER;

  if (hsptr == NULL || hsptr->liste == NULL || hsptr->anzliste == 0) { return 0; }
  if (hsptr->hg.sperr || hsptr->anzliste >= maxliste || hsptr->hg.minteil == 0 || hsptr->hg.maxteil == 0) { return 0; }
  if (hsptr->anzelem < hsptr->anzliste * hsptr->hg.maxteil) { return 0; }

  hs1.anzliste = (unsigned long)hsptr->anzelem * 100 / hsptr->hg.minteil;
  hs1.anzliste = SML3_primzahl(hs1.anzliste, 1);
  if (hs1.anzliste > maxliste) { hs1.anzliste = maxliste; }

  hs1.liste = SML3_malloc(sizeof(*hs1.liste) * hs1.anzliste);

  for (anzliste = 0; anzliste < hs1.anzliste; anzliste++) {
    hs1.liste[anzliste].max = hs1.liste[anzliste].anz = 0;
    hs1.liste[anzliste].elem = NULL;
  }

  for (anzliste = 0; anzliste < hsptr->anzliste; anzliste++) {
    if (hsptr->liste[anzliste].elem != NULL && hsptr->liste[anzliste].max > 0) {
      for (anz = 0; anz < hsptr->liste[anzliste].anz; anz++) {
        he1 = hsptr->liste[anzliste].elem[anz];
        he1->listepos = errechne_listepos(he1->key.inh, he1->key.size, hs1.anzliste);
        i1 = errechne_elempos(&ipos, &hs1, he1->listepos, he1->key.inh, he1->key.size, he1->typ, 1);
        if (i1 < 0) {
          SML3_fehleradd(NULL);
          hsptr->liste[anzliste].max = hsptr->liste[anzliste].anz = 0;
          hashdest(&hs1);
          return -1;
        }
        hs1.liste[he1->listepos].elem[ipos] = he1;
      }
      free(hsptr->liste[anzliste].elem);
      hsptr->liste[anzliste].max = hsptr->liste[anzliste].anz = 0;
      hsptr->liste[anzliste].elem = NULL;
    }
  }

  free(hsptr->liste);
  hsptr->liste = hs1.liste;
  hsptr->anzliste = hs1.anzliste;

  return 0;
} /* Ende resize_hash */


/* voriges_hashelem [reentrant]:
 * gibt voriges Hash-Element ab Listen-Position 3.Arg
 * und Element-Position 4.Arg zurueck
 * 1.Arg: Start-Hash
 * 2.Arg: Hash
 * 3.Arg: Listen-Position
 * 4.Arg: Element-Position
 * 5.Arg: Value-Typ, den voriges Hash-Element haben muss
 * 6.Arg: ob Oberhash vermeiden (0 = nein, 1 = ja)
 * Rueckgabe: voriges Hash-Element oder NULL = kein voriges vorhanden
 */
static struct SML3_hashelem *
voriges_hashelem(struct SML3_hash *hstart, struct SML3_hash *hsptr, unsigned int listepos, int anz, int typ, int noob)
{
  struct SML3_hashelem *heptr = NULL;

  for (;;) {
    for (anz--; anz >= 0; anz--) {
      if (hsptr->liste[listepos].elem[anz]->typ == typ) { heptr = hsptr->liste[listepos].elem[anz]; break; }
      if (hsptr->liste[listepos].elem[anz]->typ == HASHTYP_SUBHASH) {
        struct SML3_hash *hsu = (struct SML3_hash *)hsptr->liste[listepos].elem[anz]->val.inh;
        if (hsu->liste != NULL && hsu->anzliste > 0) {
          unsigned int l1;
          int a1;
          l1 = hsu->anzliste - 1;
          if (hsu->liste[l1].elem != NULL && hsu->liste[l1].max > 0) { a1 = hsu->liste[l1].anz; } else { a1 = 0; }
          heptr = voriges_hashelem(hstart, hsu, l1, a1, typ, 1);
          if (heptr != NULL) { break; }
        }
      }
    }
    if (anz >= 0) { break; }
    if (--listepos == (unsigned int)-1) { break; }
    if (hsptr->liste[listepos].elem != NULL && hsptr->liste[listepos].max > 0) { anz = hsptr->liste[listepos].anz; } else { anz = 0; }
  }

  if (heptr == NULL && hsptr->home != NULL && !noob && hsptr != hstart) {  /* im Oberhash suchen */
    struct SML3_hashelem *heo;
    struct SML3_hash *hso;
    int a1;
    heo = (struct SML3_hashelem *)hsptr->home;
    hso = (struct SML3_hash *)heo->home;
    for (a1 = 0; a1 < hso->liste[heo->listepos].anz; a1++) {
      if (hso->liste[heo->listepos].elem[a1] == heo) { break; }
    }
    if (a1 == hso->liste[heo->listepos].anz) { return NULL; }
    heptr = voriges_hashelem(hstart, hso, heo->listepos, a1, typ, 0);
  }

  return heptr;
} /* Ende voriges_hashelem */


/* naechstes_hashelem [reentrant]:
 * gibt naechstes Hash-Element ab Listen-Position 3.Arg
 * und Element-Position 4.Arg zurueck
 * 1.Arg: Start-Hash
 * 2.Arg: Hash
 * 3.Arg: Listen-Position
 * 4.Arg: Element-Position
 * 5.Arg: Value-Typ, den naechstes Hash-Element haben muss
 * 6.Arg: ob Unterhash vermeiden (0 = nein, 1 = ja)
 * 7.Arg: ob Oberhash vermeiden (0 = nein, 1 = ja)
 * Rueckgabe: naechstes Hash-Element oder NULL = Ende
 */
static struct SML3_hashelem *
naechstes_hashelem(struct SML3_hash *hstart, struct SML3_hash *hsptr, unsigned int listepos, int anz, int typ, int nount, int noob)
{
  struct SML3_hashelem *heptr = NULL;

  for (;;) {
    for (anz++; anz < hsptr->liste[listepos].anz; anz++) {
      if (hsptr->liste[listepos].elem[anz]->typ == typ) { heptr = hsptr->liste[listepos].elem[anz]; break; }
      if (hsptr->liste[listepos].elem[anz]->typ == HASHTYP_SUBHASH && !nount) {
        struct SML3_hash *hsu = (struct SML3_hash *)hsptr->liste[listepos].elem[anz]->val.inh;
        if (hsu->liste != NULL && hsu->anzliste > 0) {
          unsigned int l1;
          int a1;
          l1 = 0;
          if (hsu->liste[l1].elem == NULL || hsu->liste[l1].max == 0) { a1 = hsu->liste[l1].anz - 1; } else { a1 = -1; }
          heptr = naechstes_hashelem(hstart, hsu, l1, a1, typ, 0, 1);
          if (heptr != NULL) { break; }
        }
      }
    }
    if (anz < hsptr->liste[listepos].anz) { break; }
    if (++listepos == hsptr->anzliste) { break; }
    if (hsptr->liste[listepos].elem == NULL || hsptr->liste[listepos].max == 0) {
      anz = hsptr->liste[listepos].anz - 1;
    } else {
      anz = -1;
    }
  }

  if (heptr == NULL && hsptr->home != NULL && !noob && !nount && hsptr != hstart) {  /* im Oberhash suchen */
    struct SML3_hashelem *heo;
    struct SML3_hash *hso;
    int a1;
    heo = (struct SML3_hashelem *)hsptr->home;
    hso = (struct SML3_hash *)heo->home;
    for (a1 = 0; a1 < hso->liste[heo->listepos].anz; a1++) {
      if (hso->liste[heo->listepos].elem[a1] == heo) { break; }
    }
    if (a1 == hso->liste[heo->listepos].anz) { return NULL; }
    heptr = naechstes_hashelem(hstart, hso, heo->listepos, a1, typ, 0, 0);
  }

  return heptr;
} /* Ende naechstes_hashelem */


/* dump_hashelem [ohne Ausgabefunktionen: thread-sicher]:
 * Dump eines Hash-Elements nach stdout
 * 1.Arg: Hash-Element
 * 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)
 */
static void
dump_hashelem(struct SML3_hashelem *heptr, void (*keyout)(const void *, size_t), void (*valout)(const void *, size_t))
{
  const void *vptr;
  struct SML3_hash *hsptr;
  size_t s1;

  printf("Elem=%p,ListPos=%u,Typ=%c,homeHash=[%p]: ", (void *)heptr, heptr->listepos, heptr->typ, heptr->home);

  printf("Key=");
  hsptr = SML3_hashtophash(heptr->home);
  while (SML3_hashelem_keypath(&hsptr, heptr, &vptr, &s1)) {
    if (keyout != NULL && s1 > 0) {
      printf("["); keyout(vptr, s1); printf("]%s", hsptr == NULL ? ", " : " ");
    } else if (s1 == 0) {
      printf("[%lu]%s", (unsigned long)vptr, hsptr == NULL ? ", " : " ");
    } else {
      printf("[%p,%lu]%s", vptr, (unsigned long)s1, hsptr == NULL ? ", " : " ");
    }
  }

  if (heptr->typ != HASHTYP_SUBHASH) {
    printf("Val=");
    if (valout != NULL && heptr->val.inh != NULL && heptr->val.size > 0) {
      printf("["); valout(heptr->val.inh, heptr->val.size); printf("]");
    } else if (heptr->val.size == 0) {
      printf("[%lu]", (unsigned long)heptr->val.inh);
    } else {
      printf("[%p,%lu]", heptr->val.inh, (unsigned long)heptr->val.size);
    }
  } else {
    hsptr = (struct SML3_hash *)SML3_hashelem_valget(heptr, NULL);
    printf("SubHash=[%p]", (void *)hsptr);
    if (hsptr->home != heptr) {
      fprintf(stderr, "\n*** Fehler: hash->home != hashelem\n");
      abort();
    }
  }

  printf("\n");
} /* Ende dump_hashelem */


/* Dump nach stdout */
static void
hashdump(int rkpos, struct SML3_hash *hsptr, void (*keyout)(const void *, size_t), void (*valout)(const void *, size_t))
{
  int anz, i1;
  unsigned int anzliste;
  struct SML3_hashelem *he1;

  if (hsptr == NULL || hsptr->liste == NULL || hsptr->anzliste == 0) { return; }

  for (anzliste = 0; anzliste < hsptr->anzliste; anzliste++) {
    if (hsptr->liste[anzliste].max == 0) { continue; }
    for (anz = 0; anz < hsptr->liste[anzliste].anz; anz++) {
      he1 = hsptr->liste[anzliste].elem[anz];
      if (he1->home != hsptr) {
        fprintf(stderr, "*** Fehler: hashelem->home != hash\n");
        abort();
      }
      if (he1->listepos != anzliste) {
        fprintf(stderr, "*** Fehler: hashelem->listepos != anzliste\n");
        abort();
      }
      for (i1 = 0; i1 < rkpos; i1++) { printf("+"); }
      printf("H(%p,%u): ", (void *)hsptr, hsptr->anzliste);
      dump_hashelem(he1, keyout, valout);
      if (he1->typ == HASHTYP_SUBHASH) {  /* Unterhash */
        hashdump(rkpos + 1, (struct SML3_hash *)he1->val.inh, keyout, valout);
      }
    }
  }
} /* Ende hashdump */


/* erneuert rekursiv Deallozierungsfunktion */
static void
reload_destfk(struct SML3_hash *hsptr, void (*n_destfk)(void *), void (*a_destfk)(void *))
{
  struct SML3_hashelem *he1;

  if (hsptr == NULL) { return; }
  if (hsptr->destfk == a_destfk) { hsptr->destfk = n_destfk; }

  for (he1 = SML3_hashsublist(hsptr, NULL); he1 != NULL; he1 = SML3_hashsublist(hsptr, he1)) {
    reload_destfk(SML3_hashsubhash(he1), n_destfk, a_destfk);
  }
} /* Ende reload_destfk */


/* initialisiert Hash */
static void
hashinit(struct SML3_hash *hsptr, void (*destfk)(void *), unsigned int anzliste)
{
  if (hsptr == NULL) { return; }

  hsptr->home = NULL;
  hsptr->destfk = destfk;

  hsptr->hg.sperr = 0;
  hsptr->hg.minteil = 200;
  hsptr->hg.maxteil = 50;

  if (anzliste < 13) { anzliste = 13; }
  if (anzliste > maxliste) { anzliste = maxliste; }

  SML3_fehlerstop();

  hsptr->anzliste = SML3_primzahl(anzliste, 1);
  hsptr->anzelem = 0;

  hsptr->liste = malloc(sizeof(*hsptr->liste) * hsptr->anzliste);
  if (hsptr->liste == NULL) {
    hsptr->anzliste = 13;
    hsptr->liste = malloc(sizeof(*hsptr->liste) * hsptr->anzliste);
    if (hsptr->liste == NULL) { hsptr->anzliste = 0; }
  }

  SML3_fehlerstart();

  for (anzliste = 0; anzliste < hsptr->anzliste; anzliste++) {
    hsptr->liste[anzliste].max = hsptr->liste[anzliste].anz = 0;
    hsptr->liste[anzliste].elem = NULL;
  }
} /* Ende hashinit */


/* gibt Hash frei */
static void
hashdest(struct SML3_hash *hsptr)
{
  unsigned int anzliste;
  int anz;
  struct SML3_hashelem *he1;
  struct SML3_hash hs1 = HASH_INITIALIZER;

  if (hsptr == NULL) { return; }
  hs1.home = hsptr->home;
  if (hsptr->liste == NULL || hsptr->anzliste == 0) { memmove(hsptr, &hs1, sizeof(hs1)); return; }

  for (anzliste = 0; anzliste < hsptr->anzliste; anzliste++) {  /* je Hash-Element-Liste */
    if (hsptr->liste[anzliste].elem != NULL && hsptr->liste[anzliste].max > 0) {  /* Liste alloziert */
      for (anz = 0; anz < hsptr->liste[anzliste].anz; anz++) {  /* je benutztem Element */
        he1 = hsptr->liste[anzliste].elem[anz];
        /* Key freigeben */
        if (he1->key.inh != NULL && he1->key.size > 0) { free(he1->key.inh); }
        /* je nach Typ Value freigeben */
        if (he1->val.inh != NULL) {
          if (he1->typ == HASHTYP_SUBHASH) {  /* Unterhash */
            hashdest((struct SML3_hash *)he1->val.inh);
          } else if (he1->typ == HASHTYP_NORMAL) {  /* normaler Eintrag */
            if (hsptr->destfk != NULL) { SML3_fehlerstop(); hsptr->destfk(he1->val.inh); SML3_fehlerstart(); }
          }
          if (he1->val.size > 0) { free(he1->val.inh); }
        }
        /* Element freigeben */
        free(he1);
      }
      /* Elementliste freigeben */
      free(hsptr->liste[anzliste].elem);
    }
  }
  free(hsptr->liste);
  memmove(hsptr, &hs1, sizeof(hs1));
} /* Ende hashdest */


/* kopiert Hash */
static void
hashcopy(struct SML3_hash *hsz, struct SML3_hash *hsq, void (*copyfk)(void *))
{
  unsigned int anzliste;
  int anz;
  struct SML3_hash hs1 = HASH_INITIALIZER;

  if (hsz == NULL || hsq == NULL) { return; }
  hs1.home = hsz->home;

  hashdest(hsz);
  memmove(hsz, hsq, sizeof(struct SML3_hash));
  hsz->home = hs1.home;
  hsz->hg.sperr = 0;
  if (hsz->liste == NULL || hsz->anzliste == 0) { return; }

  hsz->liste = SML3_malloc(sizeof(*hsz->liste) * hsz->anzliste);

  for (anzliste = 0; anzliste < hsz->anzliste; anzliste++) {  /* je Hash-Element-Liste */
    hsz->liste[anzliste].max = hsq->liste[anzliste].max;
    hsz->liste[anzliste].anz = hsq->liste[anzliste].anz;
    hsz->liste[anzliste].elem = NULL;

    if (hsz->liste[anzliste].max > 0) {  /* Liste ist alloziert */
      hsz->liste[anzliste].elem = SML3_malloc(sizeof(struct SML3_hashelem *) * hsz->liste[anzliste].max);

      for (anz = 0; anz < hsz->liste[anzliste].anz; anz++) {  /* je Listen-Element */
        hsz->liste[anzliste].elem[anz] = SML3_calloc(1, sizeof(struct SML3_hashelem));

        hsz->liste[anzliste].elem[anz]->prev = NULL;
        hsz->liste[anzliste].elem[anz]->next = NULL;
        hsz->liste[anzliste].elem[anz]->home = hsz;
        hsz->liste[anzliste].elem[anz]->listepos = anzliste;
        hsz->liste[anzliste].elem[anz]->typ = hsq->liste[anzliste].elem[anz]->typ;

        /* Key kopieren */
        hsz->liste[anzliste].elem[anz]->key.size = hsq->liste[anzliste].elem[anz]->key.size;
        if (hsz->liste[anzliste].elem[anz]->key.size > 0) {
          hsz->liste[anzliste].elem[anz]->key.inh = SML3_malloc(hsz->liste[anzliste].elem[anz]->key.size);
          memmove(hsz->liste[anzliste].elem[anz]->key.inh,
            hsq->liste[anzliste].elem[anz]->key.inh, hsz->liste[anzliste].elem[anz]->key.size);
        } else {
          hsz->liste[anzliste].elem[anz]->key.inh = hsq->liste[anzliste].elem[anz]->key.inh;
        }

        /* Value kopieren */
        hsz->liste[anzliste].elem[anz]->val.size = hsq->liste[anzliste].elem[anz]->val.size;
        if (hsz->liste[anzliste].elem[anz]->val.size > 0) {
          hsz->liste[anzliste].elem[anz]->val.inh = SML3_malloc(hsz->liste[anzliste].elem[anz]->val.size);
          if (hsz->liste[anzliste].elem[anz]->typ == HASHTYP_SUBHASH) {  /* Unterhash */
            struct SML3_hash *hsuz = (struct SML3_hash *)hsz->liste[anzliste].elem[anz]->val.inh;
            struct SML3_hash *hsuq = (struct SML3_hash *)hsq->liste[anzliste].elem[anz]->val.inh;
            memmove(hsuz, &hs1, sizeof(hs1));
            hsuz->home = hsz->liste[anzliste].elem[anz];
            hashcopy(hsuz, hsuq, copyfk);
          } else if (hsz->liste[anzliste].elem[anz]->typ == HASHTYP_NORMAL) {  /* normaler Eintrag */
            memmove(hsz->liste[anzliste].elem[anz]->val.inh,
              hsq->liste[anzliste].elem[anz]->val.inh, hsz->liste[anzliste].elem[anz]->val.size);
            if (copyfk != NULL) {  /* Value hat vermutlich allozierte Pointer */
              copyfk(hsz->liste[anzliste].elem[anz]->val.inh);
            }
          } else {
            memmove(hsz->liste[anzliste].elem[anz]->val.inh,
              hsq->liste[anzliste].elem[anz]->val.inh, hsz->liste[anzliste].elem[anz]->val.size);
          }
        } else {
          hsz->liste[anzliste].elem[anz]->val.inh = hsq->liste[anzliste].elem[anz]->val.inh;
        }
      }
    }
  }
} /* Ende hashcopy */


/* 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
 */
struct SML3_hash *
SML3_hashnew(void (*destfk)(void *), unsigned int anzliste)
{
  struct SML3_hash *hsptr;

  hsptr = SML3_calloc(1, sizeof(struct SML3_hash));
  hashinit(hsptr, destfk, anzliste);

  return hsptr;
} /* Ende SML3_hashnew */


/* 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)
 */
void
SML3_hashresizeparam(struct SML3_hash *hsptr, unsigned int minteil, unsigned int maxteil)
{
  if (hsptr == NULL) { return; }
  if (minteil > (unsigned short)-1) { minteil = (unsigned short)-1; }
  if (maxteil > (unsigned short)-1) { maxteil = (unsigned short)-1; }
  hsptr->hg.minteil = minteil;
  hsptr->hg.maxteil = maxteil;
} /* Ende SML3_hashresizeparam */


/* 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())
 */
void
SML3_hash_reload_destfk(struct SML3_hash *hsptr, void (*destfk)(void *))
{
  if (hsptr != NULL) { reload_destfk(hsptr, destfk, hsptr->destfk); }
} /* Ende SML3_hash_reload_destfk */


/* SML3_hashfree [ohne Deallozierungsfunktion: thread-sicher]:
 * gibt Hash frei
 * 1.Arg: Adresse auf Hash
 */
void
SML3_hashfree(struct SML3_hash **hspptr)
{
  if (hspptr == NULL || *hspptr == NULL) { return; }
  hashdest(*hspptr);
  free(*hspptr);
  *hspptr = NULL;
} /* Ende SML3_hashfree */


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

  if (hsptr == NULL) { return; }
  if (hsptr->liste == NULL || hsptr->anzliste == 0) { hsptr->hg.sperr = 0; return; }

  memmove(&hs1, hsptr, sizeof(hs1));
  hashdest(hsptr);
  hashinit(hsptr, hs1.destfk, hs1.anzliste > 1000 ? 1000 : hs1.anzliste);
  hsptr->home = hs1.home;
  hsptr->hg.minteil = hs1.hg.minteil;
  hsptr->hg.maxteil = hs1.hg.maxteil;
} /* Ende SML3_hashclear */


/* 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
 */
struct SML3_hash *
SML3_hashcopy(struct SML3_hash *hsq, void (*copyfk)(void *))
{
  struct SML3_hash *hsz;

  hsz = SML3_calloc(1, sizeof(struct SML3_hash));
  hashcopy(hsz, hsq, copyfk);

  return hsz;
} /* Ende SML3_hashcopy */


/* SML3_hashswap [thread-sicher]:
 * vertauscht Inhalte beider Hashs
 * 1.+2.Arg: Hashs (beide muessen zumindest initialisiert sein)
 */
void
SML3_hashswap(struct SML3_hash *hs1, struct SML3_hash *hs2)
{
  unsigned int anzliste;
  int anz;
  struct SML3_hash hs1t = HASH_INITIALIZER;
  struct SML3_hash hs2t = HASH_INITIALIZER;

  if (hs1 == NULL || hs2 == NULL) { return; }

  memmove(&hs1t, hs1, sizeof(struct SML3_hash));
  hs1t.home = hs2->home;
  if (hs1t.liste == NULL) { hs1t.anzliste = 0; }
  for (anzliste = 0; anzliste < hs1t.anzliste; anzliste++) {
    if (hs1t.liste[anzliste].max == 0) { continue; }
    for (anz = 0; anz < hs1t.liste[anzliste].anz; anz++) {
      hs1t.liste[anzliste].elem[anz]->home = hs2;
    }
  }

  memmove(&hs2t, hs2, sizeof(struct SML3_hash));
  hs2t.home = hs1->home;
  if (hs2t.liste == NULL) { hs2t.anzliste = 0; }
  for (anzliste = 0; anzliste < hs2t.anzliste; anzliste++) {
    if (hs2t.liste[anzliste].max == 0) { continue; }
    for (anz = 0; anz < hs2t.liste[anzliste].anz; anz++) {
      hs2t.liste[anzliste].elem[anz]->home = hs1;
    }
  }

  memmove(hs1, &hs2t, sizeof(struct SML3_hash));
  memmove(hs2, &hs1t, sizeof(struct SML3_hash));
} /* Ende SML3_hashswap */


/* 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
 */
struct SML3_hashelem *
SML3_hashget(struct SML3_hash *hsptr, const void *keyptr, size_t keysize)
{
  struct SML3_hashelem *he1;

  if (hsptr == NULL) { return NULL; }
  if (hsptr->liste == NULL || hsptr->anzliste == 0) { return NULL; }

  he1 = erhalte_hashelem(hsptr, errechne_listepos(keyptr, keysize, hsptr->anzliste), keyptr, keysize, (int)HASHTYP_NORMAL, 0);
  return he1;
} /* Ende SML3_hashget */


/* 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);
 */
struct SML3_hashelem *
SML3_hashset(struct SML3_hash *hsptr, const void *keyptr, size_t keysize)
{
  struct SML3_hashelem *he1;

  if (hsptr == NULL) { SML3_fehlerexit("%s", SML3_strerror(EINVAL)); }

  if (hsptr->liste == NULL || hsptr->anzliste == 0) {  /* Erstallokation */
    unsigned int anzliste;
    hsptr->anzliste = startliste;
    hsptr->liste = SML3_malloc(sizeof(*hsptr->liste) * hsptr->anzliste);
    for (anzliste = 0; anzliste < hsptr->anzliste; anzliste++) {
      hsptr->liste[anzliste].max = hsptr->liste[anzliste].anz = 0;
      hsptr->liste[anzliste].elem = NULL;
    }
  }

  he1 = erhalte_hashelem(hsptr, errechne_listepos(keyptr, keysize, hsptr->anzliste), keyptr, keysize, (int)HASHTYP_NORMAL, 1);
  if (he1 == NULL) { SML3_fehlerexit(NULL); }
  return he1;
} /* Ende SML3_hashset */


/* 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.
 */
struct SML3_hashelem *
SML3_hashdel(struct SML3_hashelem *heptr)
{
  return SML3_hashsubdel(NULL, heptr);
} /* Ende SML3_hashdel */


/* 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
 */
struct SML3_hashelem *
SML3_hashlist(struct SML3_hash *hsptr, struct SML3_hashelem *heptr, int gosub)
{
  int anz;
  unsigned int listepos;
  struct SML3_hash *hs1;

  if (hsptr == NULL || hsptr->liste == NULL || hsptr->anzliste == 0) { return NULL; }

  if (heptr != NULL) {
    hs1 = heptr->home;
    listepos = heptr->listepos;
    if (hs1->liste[listepos].elem == NULL || hs1->liste[listepos].max == 0) { return NULL; }
    /* Element suchen */
    for (anz = 0; anz < hs1->liste[listepos].anz; anz++) {
      if (hs1->liste[listepos].elem[anz] == heptr) { break; }
    }
    if (anz == hs1->liste[listepos].anz) { return NULL; }
  } else {
    hs1 = hsptr;
    listepos = 0;
    anz = -1;
  }

  /* naechstes Hash-Element */
  heptr = naechstes_hashelem(hsptr, hs1, listepos, anz, (int)HASHTYP_NORMAL, !gosub, 0);

  return heptr;
} /* Ende SML3_hashlist */


/* 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
 */
struct SML3_hash *
SML3_hashsubget(struct SML3_hash *hsptr, const void *keyptr, size_t keysize)
{
  struct SML3_hashelem *he1;

  if (hsptr == NULL || hsptr->liste == NULL || hsptr->anzliste == 0) { return NULL; }

  he1 = erhalte_hashelem(hsptr, errechne_listepos(keyptr, keysize, hsptr->anzliste), keyptr, keysize, (int)HASHTYP_SUBHASH, 0);
  if (he1 == NULL) { return NULL; }

  return (struct SML3_hash *)he1->val.inh;
} /* Ende SML3_hashsubget */


/* 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_hashfree(&h1);
 */
struct SML3_hash *
SML3_hashsubset(struct SML3_hash *hsptr, const void *keyptr, size_t keysize)
{
  struct SML3_hashelem *he1;

  if (hsptr == NULL) { SML3_fehlerexit("%s", SML3_strerror(EINVAL)); }

  if (hsptr->liste == NULL || hsptr->anzliste == 0) {  /* Erstallokation */
    unsigned int anzliste;
    hsptr->anzliste = startliste;
    hsptr->liste = SML3_malloc(sizeof(*hsptr->liste) * hsptr->anzliste);
    for (anzliste = 0; anzliste < hsptr->anzliste; anzliste++) {
      hsptr->liste[anzliste].max = hsptr->liste[anzliste].anz = 0;
      hsptr->liste[anzliste].elem = NULL;
    }
  }

  he1 = erhalte_hashelem(hsptr, errechne_listepos(keyptr, keysize, hsptr->anzliste), keyptr, keysize, (int)HASHTYP_SUBHASH, 1);
  if (he1 == NULL) { SML3_fehlerexit(NULL); }

  if (he1->val.inh == NULL) {  /* in val Unter-Hash anlegen */
    struct SML3_hash *hsneu;
    struct SML3_hash hs1 = HASH_INITIALIZER;
    he1->val.inh = SML3_malloc(sizeof(struct SML3_hash));
    he1->val.size = sizeof(struct SML3_hash);
    memmove(he1->val.inh, &hs1, he1->val.size);
    hsneu = (struct SML3_hash *)he1->val.inh;
    hsneu->home = he1;
    hsneu->destfk = ((struct SML3_hash *)he1->home)->destfk;
    hsneu->hg.minteil = ((struct SML3_hash *)he1->home)->hg.minteil;
    hsneu->hg.maxteil = ((struct SML3_hash *)he1->home)->hg.maxteil;
  }

  return (struct SML3_hash *)he1->val.inh;
} /* Ende SML3_hashsubset */


/* 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
 */
struct SML3_hashelem *
SML3_hashsubdel(struct SML3_hash *hstart, struct SML3_hashelem *heptr)
{
  struct SML3_hash *hs1, *hs0;
  int anz, i1;
  unsigned int listepos;
  char typ;

  if (heptr == NULL || heptr->home == NULL) { return NULL; }
  hs1 = heptr->home;
  if (hstart != NULL) {
    for (hs0 = hs1; ;) {
      if (hs0 == hstart) { break; }
      if (hs0->home == NULL) { return NULL; }  /* heptr ist ausserhalb hstart */
      hs0 = (struct SML3_hash *)((struct SML3_hashelem *)hs0->home)->home;
    }
  }
  listepos = heptr->listepos;
  typ = heptr->typ;
  if (hs1->liste[listepos].elem == NULL || hs1->liste[listepos].max == 0) { return NULL; }

  /* Element suchen */
  for (anz = 0; anz < hs1->liste[listepos].anz; anz++) {
    if (hs1->liste[listepos].elem[anz] == heptr) { break; }
  }
  if (anz == hs1->liste[listepos].anz) { return NULL; }

  /* Key freigeben */
  if (heptr->key.inh != NULL && heptr->key.size > 0) { free(heptr->key.inh); }
  /* je nach Typ Value freigeben */
  if (heptr->val.inh != NULL) {
    if (typ == HASHTYP_SUBHASH) {  /* Unterhash */
      hashdest((struct SML3_hash *)heptr->val.inh);
    } else if (typ == HASHTYP_NORMAL) {  /* normaler Eintrag */
      if (hs1->destfk != NULL) { SML3_fehlerstop(); hs1->destfk(heptr->val.inh); SML3_fehlerstart(); }
    }
    if (heptr->val.size > 0) { free(heptr->val.inh); }
  }
  /* Element freigeben */
  free(heptr);
  hs1->anzelem--;

  /* Liste schieben */
  hs1->liste[listepos].anz--;
  i1 = hs1->liste[listepos].anz - anz;
  if (i1 > 0) {
    memmove(&hs1->liste[listepos].elem[anz], &hs1->liste[listepos].elem[anz + 1], sizeof(struct SML3_hashelem *) * i1);
  }

  /* voriges Hash-Element gleichen Typs erhalten */
  for (hs0 = hs1; ;) {
    if (hstart != NULL && hs0 == hstart) { break; }
    if (hs0->home == NULL) {
      if (hstart != NULL) { return NULL; }  /* kein voriges Element unterhalb hstart */
      break;
    }
    hs0 = (struct SML3_hash *)((struct SML3_hashelem *)hs0->home)->home;
  }
  heptr = voriges_hashelem(hs0, hs1, listepos, anz, typ, 0);

  return heptr;
} /* Ende SML3_hashsubdel */


/* SML3_hashsublist [thread-sicher]:
 * gibt naechstes Hash-Element fuer ein Unter-Hash des 1.Args zurueck
 * 1.Arg: Hash
 * 2.Arg: aktuelles Hash-Element oder NULL = ab Beginn
 * Rueckgabe: naechstes Unter-Hash-Element oder NULL = Ende
 */
struct SML3_hashelem *
SML3_hashsublist(struct SML3_hash *hsptr, struct SML3_hashelem *heptr)
{
  int anz;
  unsigned int listepos;
  struct SML3_hash *hs1;

  if (hsptr == NULL || hsptr->liste == NULL || hsptr->anzliste == 0) { return NULL; }

  if (heptr != NULL) {
    hs1 = heptr->home;
    listepos = heptr->listepos;
    if (hs1->liste[listepos].elem == NULL || hs1->liste[listepos].max == 0) { return NULL; }
    /* Element suchen */
    for (anz = 0; anz < hs1->liste[listepos].anz; anz++) {
      if (hs1->liste[listepos].elem[anz] == heptr) { break; }
    }
    if (anz == hs1->liste[listepos].anz) { return NULL; }
  } else {
    hs1 = hsptr;
    listepos = 0;
    anz = -1;
  }

  /* naechstes Unter-Hash-Element */
  heptr = naechstes_hashelem(hsptr, hs1, listepos, anz, (int)HASHTYP_SUBHASH, 0, 0);  /* kann nur oberste Ebene */

  return heptr;
} /* Ende SML3_hashsublist */


/* 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())
 */
void *
SML3_hashelem_valget(struct SML3_hashelem *heptr, size_t *valsize)
{
  if (heptr == NULL) {
    if (valsize != NULL) { *valsize = 0; }
    return NULL;
  }
  if (valsize != NULL) { *valsize = heptr->val.size; }
  return heptr->val.inh;
} /* Ende SML3_hashelem_valget */


/* 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);
 *   struct 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_hashfree(&h1);
 */
void
SML3_hashelem_valset(struct SML3_hashelem *heptr, const void *valptr, size_t valsize)
{
  void *valp = (void *)valptr;
  size_t vals = valsize;

  if (heptr == NULL) { SML3_fehlerexit("%s", SML3_strerror(EINVAL)); }
  if (heptr->typ == HASHTYP_SUBHASH) { SML3_fehlerexit("%s", SML3_strerror(EISDIR)); }

  if (valptr != NULL && valsize > 0) {
    valp = SML3_malloc(valsize);
    memmove(valp, valptr, valsize);
  }

  /* je nach Typ Value freigeben */
  if (heptr->val.inh != NULL && heptr->val.size > 0) {
    if (heptr->typ == HASHTYP_NORMAL) {  /* normaler Eintrag */
      struct SML3_hash *hs1 = heptr->home;
      if (hs1->destfk != NULL) { SML3_fehlerstop(); hs1->destfk(heptr->val.inh); SML3_fehlerstart(); }
    }
    free(heptr->val.inh);
  }

  heptr->val.inh = valp;
  heptr->val.size = vals;
} /* Ende SML3_hashelem_valset */


/* 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
 */
void
SML3_hashelem_valswap(struct SML3_hashelem *heptr, void **valpptr, size_t *valpsize)
{
  void *v1;
  size_t s1;

  if (heptr == NULL || valpptr == NULL || valpsize == NULL) { return; }
  if (heptr->typ == HASHTYP_SUBHASH) { return; }

  v1 = heptr->val.inh;
  s1 = heptr->val.size;
  if (*valpptr == v1 && *valpsize == s1) { *valpptr = NULL; *valpsize = 0; return; }

  heptr->val.inh = *valpptr;
  heptr->val.size = *valpsize;

  *valpptr = v1;
  *valpsize = s1;
} /* Ende SML3_hashelem_valswap */


/* 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)
 *            bzw. Zahl (siehe SML3_hashset() bzw. SML3_hashsubset())
 */
const void *
SML3_hashelem_keyget(struct SML3_hashelem *heptr, size_t *keysize)
{
  if (heptr == NULL) {
    if (keysize != NULL) { *keysize = 0; }
    return NULL;
  }
  if (keysize != NULL) { *keysize = heptr->key.size; }
  return heptr->key.inh;
} /* Ende SML3_hashelem_keyget */


/* 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
 */
int
SML3_hashelem_keypath(struct SML3_hash **hspptr, struct SML3_hashelem *heptr, const void **keyptr, size_t *keysize)
{
  struct SML3_hash *hs1;
  struct SML3_hashelem *he1;

  if (keyptr != NULL) { *keyptr = NULL; }
  if (keysize != NULL) { *keysize = 0; }
  if (hspptr == NULL || *hspptr == NULL || heptr == NULL) { return 0; }

  for (he1 = heptr; ;) {
    hs1 = (struct SML3_hash *)he1->home;
    if (hs1 == NULL) { return 0; }
    if (hs1 == *hspptr) { break; }
    he1 = (struct SML3_hashelem *)hs1->home;
    if (he1 == NULL) { return 0; }
  }
  if (he1 != heptr) { *hspptr = (struct SML3_hash *)he1->val.inh; } else { *hspptr = NULL; }

  if (keyptr != NULL) { *keyptr = he1->key.inh; }
  if (keysize != NULL) { *keysize = he1->key.size; }
  return 1;
} /* Ende SML3_hashelem_keypath */


/* 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)
 */
struct SML3_hash *
SML3_hashelem_hashget(struct SML3_hashelem *heptr)
{
  if (heptr == NULL) { return NULL; }
  return (struct SML3_hash *)heptr->home;
} /* Ende SML3_hashelem_hashget */


/* 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
 */
int
SML3_hashelem_typ_is_hash(struct SML3_hashelem *heptr)
{
  if (heptr == NULL) { return 0; }
  if (heptr->typ != HASHTYP_SUBHASH) { return 0; }
  return 1;
} /* Ende SML3_hashelem_typ_is_hash */


/* SML3_hashelem_typ_is_normal [reentrant]:
 * testet, ob Hash-Element vom Typ=Normal ist
 * 1.Arg: Hash-Element
 * Rueckgabe: 1 = ja, 0 = nein
 */
int
SML3_hashelem_typ_is_normal(struct SML3_hashelem *heptr)
{
  if (heptr == NULL) { return 0; }
  if (heptr->typ != HASHTYP_NORMAL) { return 0; }
  return 1;
} /* Ende SML3_hashelem_typ_is_normal */


/* 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
 */
struct SML3_hashelem *
SML3_hashparentelem(struct SML3_hash *hsptr)
{
  if (hsptr == NULL) { return NULL; }
  return (struct SML3_hashelem *)hsptr->home;
} /* Ende SML3_hashparentelem */


/* 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)
 */
struct SML3_hash *
SML3_hashsubhash(struct SML3_hashelem *heptr)
{
  if (heptr == NULL) { return NULL; }
  if (heptr->typ != HASHTYP_SUBHASH) { return NULL; }
  return (struct SML3_hash *)SML3_hashelem_valget(heptr, NULL);
} /* Ende SML3_hashsubhash */


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

  if (hsptr == NULL) { return NULL; }

  for (;;) {
    he1 = (struct SML3_hashelem *)hsptr->home;
    if (he1 == NULL) { break; }
    hsptr = (struct SML3_hash *)he1->home;
  }

  return hsptr;
} /* Ende SML3_hashtophash */


/* 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);
 */
void
SML3_hashelem_keyarray(struct SML3_hash *hsptr, struct SML3_hashelem *heptr, struct SML3_hash_keyarray *karry)
{
  int max, skip;
  struct SML3_hash *hp;

  if (karry == NULL) { return; }
  if (karry->e != NULL) {
    if (heptr == NULL) {
      free(karry->e);
      memset(karry, 0, sizeof(*karry));
    } else {
      for (skip = 0; skip < karry->anz; skip++) { karry->e[skip].keyptr = NULL; karry->e[skip].keysize = 0; }
    }
  }
  if (heptr == NULL) { karry->anz = 0; return; }

  max = 2;
  if (karry->e != NULL && karry->anz >= max) { max = karry->anz; }
  if (karry->e == NULL || karry->anz < max) {
    if (karry->e != NULL) {
      karry->e = SML3_realloc(karry->e, sizeof(*karry->e) * max);
    } else {
      karry->e = SML3_malloc(sizeof(*karry->e) * max);
    }
  }
  karry->anz = 0;

  hp = SML3_hashtophash(SML3_hashelem_hashget(heptr));
  if (hsptr != NULL && hp != hsptr) { skip = 1; } else { skip = 0; }

  while (SML3_hashelem_keypath(&hp, heptr, &karry->e[karry->anz].keyptr, &karry->e[karry->anz].keysize)) {
    if (skip) {
      if (hp == hsptr) { skip = 0; }
      continue;
    }
    if (++karry->anz >= max) {
      max = karry->anz + 2;
      karry->e = SML3_realloc(karry->e, sizeof(*karry->e) * max);
    }
  }

  if (SML3_hashelem_typ_is_hash(heptr)) { karry->ishash = 1; } else { karry->ishash = 0; }
} /* Ende SML3_hashelem_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
 */
struct SML3_hashelem *
SML3_hashgetarray(struct SML3_hash *hsptr, struct SML3_hash_keyarray *karry)
{
  int anz;
  struct SML3_hashelem *he1 = NULL;

  if (hsptr == NULL || karry == NULL || karry->anz < 1) { return NULL; }

  for (anz = 0; anz < karry->anz; anz++) {
    if (anz < karry->anz - 1 || karry->ishash) {
      hsptr = SML3_hashsubget(hsptr, karry->e[anz].keyptr, karry->e[anz].keysize);
      if (hsptr == NULL) { return NULL; }
    } else {
      he1 = SML3_hashget(hsptr, karry->e[anz].keyptr, karry->e[anz].keysize);
      if (he1 == NULL) { return NULL; }
    }
  }

  if (karry->ishash) { he1 = SML3_hashparentelem(hsptr); }

  return he1;
} /* Ende SML3_hashgetarray */


/* 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 ...
 *   }
 */
struct SML3_hashelem *
SML3_hashsetarray(struct SML3_hash *hsptr, struct SML3_hash_keyarray *karry)
{
  int anz;
  struct SML3_hashelem *he1 = NULL;

  if (hsptr == NULL) { SML3_fehlerexit("%s", SML3_strerror(EINVAL)); }
  if (karry == NULL || karry->anz < 1) { return NULL; }

  for (anz = 0; anz < karry->anz; anz++) {
    if (anz < karry->anz - 1 || karry->ishash) {
      hsptr = SML3_hashsubset(hsptr, karry->e[anz].keyptr, karry->e[anz].keysize);
    } else {
      he1 = SML3_hashset(hsptr, karry->e[anz].keyptr, karry->e[anz].keysize);
    }
  }

  if (karry->ishash) { he1 = SML3_hashparentelem(hsptr); }

  return he1;
} /* Ende SML3_hashsetarray */


/* 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)
 */
void
SML3_hashsort(struct SML3_hash *hsptr, int (*qscmp)(const void *, const void *), int gosub)
{
  struct SML3_hashelem *he1, **hefeld;
  size_t s1, sanz;

  if (hsptr == NULL) { return; }
  if (qscmp == NULL) {
    for (he1 = SML3_hashlist(hsptr, NULL, gosub); he1 != NULL; he1 = SML3_hashlist(hsptr, he1, gosub)) {
      he1->prev = he1->next = NULL;
    }
    return;
  }

  sanz = 0;
  for (he1 = SML3_hashlist(hsptr, NULL, gosub); he1 != NULL; he1 = SML3_hashlist(hsptr, he1, gosub)) { sanz++; }
  if (sanz == 0) { return; }

  hefeld = SML3_malloc(sizeof(struct SML3_hashelem *) * sanz);

  s1 = 0;
  for (he1 = SML3_hashlist(hsptr, NULL, gosub); he1 != NULL; he1 = SML3_hashlist(hsptr, he1, gosub)) {
    hefeld[s1++] = he1;
  }

  qsort(hefeld, sanz, sizeof(struct SML3_hashelem *), qscmp);

  for (s1 = 0; s1 < sanz; s1++) {
    if (s1 > 0) { hefeld[s1]->prev = hefeld[s1 - 1]; } else { hefeld[s1]->prev = NULL; }
    if (s1 < sanz - 1) { hefeld[s1]->next = hefeld[s1 + 1]; } else { hefeld[s1]->next = NULL; }
  }

  free(hefeld);
} /* Ende SML3_hashsort */


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

  if (hsptr == NULL) { return NULL; }

  for (he1 = SML3_hashlist(hsptr, NULL, 0); he1 != NULL; he1 = SML3_hashlist(hsptr, he1, 0)) {
    if (he1->prev == NULL) { break; }
  }
  if (he1 == NULL) {
    for (he1 = SML3_hashlist(hsptr, NULL, 1); he1 != NULL; he1 = SML3_hashlist(hsptr, he1, 1)) {
      if (he1->prev == NULL) { break; }
    }
  }

  return he1;
} /* Ende SML3_hashsort_first */


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

  if (hsptr == NULL) { return NULL; }

  for (he1 = SML3_hashlist(hsptr, NULL, 0); he1 != NULL; he1 = SML3_hashlist(hsptr, he1, 0)) {
    if (he1->next == NULL) { break; }
  }
  if (he1 == NULL) {
    for (he1 = SML3_hashlist(hsptr, NULL, 1); he1 != NULL; he1 = SML3_hashlist(hsptr, he1, 1)) {
      if (he1->next == NULL) { break; }
    }
  }

  return he1;
} /* Ende SML3_hashsort_last */


/* SML3_hashsort_next [reentrant]:
 * gibt naechstes Element eines sortierten Hashs zurueck
 * 1.Arg: Hash-Element
 * Rueckgabe: naechstes Element oder NULL = Ende
 */
struct SML3_hashelem *
SML3_hashsort_next(struct SML3_hashelem *heptr)
{
  if (heptr == NULL) { return NULL; }
  return heptr->next;
} /* Ende SML3_hashsort_next */


/* SML3_hashsort_prev [reentrant]:
 * gibt voriges Element eines sortierten Hashs zurueck
 * 1.Arg: Hash-Element
 * Rueckgabe: voriges Element oder NULL = Ende
 */
struct SML3_hashelem *
SML3_hashsort_prev(struct SML3_hashelem *heptr)
{
  if (heptr == NULL) { return NULL; }
  return heptr->prev;
} /* Ende SML3_hashsort_prev */


/* 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)
 */
void
SML3_hashdump(struct SML3_hash *hsptr, void (*keyout)(const void *, size_t), void (*valout)(const void *, size_t))
{
  hashdump(0, hsptr, keyout, valout);
} /* Ende SML3_hashdump */
