/* sml3_dynfeld.c: Funktionen fuer Feldaufbau (dynamisches Feld) */

/* 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_dynfeld.h"

void SML3_dynfeld_init(struct SML3_dynfeld *, size_t, size_t);
void SML3_dynfeld_add(struct SML3_dynfeld *, void *);
void * SML3_dynfeld_get(struct SML3_dynfeld *, int *);
void SML3_freefeld(void *, void (*)(void *));
void SML3_freefeldaddr(void *, size_t, int, void (*)(void *));


/* SML3_dynfeld_init [reentrant]:
 * initialisiert dyn.Feld
 * muss einmalig vor Verwendung des dyn.Feldes aufgerufen werden,
 * alternativ kann SML3_DYNFELD_INITIALIZER() verwendet werden
 * 1.Arg: Adresse eines dyn.Feldes
 * 2.Arg: Elementgroesse
 * 3.Arg: Elementanzahl fuer blockweise Allokation
 */
void
SML3_dynfeld_init(struct SML3_dynfeld *df, size_t size, size_t block)
{
  if (df == NULL) { return; }
  df->feld = NULL;
  df->max = df->mom = 0;
  if ((df->size = size) < 1) { df->size = 1; }
  if ((df->block = block) < 1) { df->block = 1; }
} /* Ende SML3_dynfeld_init */


/* SML3_dynfeld_add [thread-sicher]:
 * fuegt ein Element dem dyn.Feld hinzu
 * 1.Arg: Adresse eines dyn.Feldes
 * 2.Arg: Adresse auf hinzuzufuegendes Element
 */
void
SML3_dynfeld_add(struct SML3_dynfeld *df, void *elem)
{
  if (df == NULL || elem == NULL) { return; }

  if (df->feld == NULL) {
    df->max = df->block;
    df->feld = SML3_malloc(df->size * (df->max + 1));
  } else if (df->mom == df->max) {
    df->max += df->block;
    df->feld = SML3_realloc(df->feld, df->size * (df->max + 1));
  }

  memmove((char *)df->feld + (df->size * df->mom), elem, df->size);
  df->mom++;
  memset((char *)df->feld + (df->size * df->mom), 0, df->size);
} /* Ende SML3_dynfeld_add */


/* SML3_dynfeld_get [reentrant]:
 * gibt Feldpointer eines dyn.Feldes zurueck,
 * das Feld ist mit einem ausgenulltem Element terminiert
 * 1.Arg: Adresse eines dyn.Feldes
 * 2.Arg: Adresse fuer Rueckgabe Anzahl Elemente oder NULL
 * Rueckgabe: Feldpointer (oder NULL = kein Feld vorhanden)
 */
void *
SML3_dynfeld_get(struct SML3_dynfeld *df, int *len)
{
  if (df == NULL) { return NULL; }
  if (len != NULL) { *len = df->mom; }
  return df->feld;
} /* Ende SML3_dynfeld_get */


/* SML3_freefeld [ohne Deallokationsfunktion: thread-sicher]:
 * dealloziert ein NULL-terminiertes Feld,
 * wobei die Deallokationsfunktion ein Element des Feldes erwartet
 * 1.Arg: Feld
 * 2.Arg: Deallokationsfunktion (1.Parameter: Element des Feldes)
 *        oder NULL, wenn das Element keine freizugebenden Inhalte hat
 */
void
SML3_freefeld(void *feld, void (*freeptr)(void *))
{
  void **ffeld = (void **)feld, **pff;

  if (feld == NULL) { return; }
  if (freeptr == free) { freeptr = NULL; }

  for (pff = ffeld; *pff != NULL; pff++) {
    if (freeptr != NULL) { freeptr(*pff); }
    free(*pff);
  }
  free(feld);
} /* Ende SML3_freefeld */


/* SML3_freefeldaddr [ohne Deallokationsfunktion: thread-sicher]:
 * dealloziert ein Feld,
 * wobei die Deallokationsfunktion die Adresse auf ein Element des Feldes erwartet
 * 1.Arg: Feld
 * 2.Arg: Elementgroesse
 * 3.Arg: Anzahl Elemente
 * 4.Arg: Deallokationsfunktion (1.Parameter: Adresse auf Element des Feldes)
 *        oder NULL, wenn das Element keine freizugebenden Inhalte hat
 */
void
SML3_freefeldaddr(void *feld, size_t size, int max, void (*freeptr)(void *))
{
  if (feld == NULL) { return; }
  if (freeptr == free) { freeptr = NULL; }

  if (freeptr != NULL && size > 0 && max > 0) {
    char *ffeld = (char *)feld;
    int pos;
    for (pos = 0; pos < max; pos++) { freeptr(&ffeld[pos * size]); }
  }
  free(feld);
} /* Ende SML3_freefeldaddr */
