#ifndef SML3__DNYFELD_H_
#define SML3__DNYFELD_H_

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

  Funktionen fuer den Aufbau eines Feldes (dynamisches Feld).

  Mit SML3_dynfeld_init() oder SML3_DYNFELD_INITIALIZER() wird das dyn.Feld initialisiert,
  dabei ist die Elementgroesse die Groesse eines jeweils spaeter hinzugefuegten Elements.

  Mit SML3_dynfeld_add() wird ein Element hinzugefuegt
  - das ein Pointer auf einen allozierten Bereich sein kann (ergibt 2-dim. Feld)
  - das ein Bereich (struct oder Integer o.ae.) selber sein kann (ergibt 1-dim. Feld)

  Mit SML3_dynfeld_get() erhaelt man das aufgebaute (1- oder 2-dim.) Feld,
  es ist mit einem ausgenullten Element beendet.

  SML3_freefeld() und SML3_freefeldaddr() deallozieren das aufgebaute Feld.
  Dabei wird SML3_freefeld() verwendet
  - wenn das jeweilige Element direkt an die Deallokationsfunktion uebergeben wird
    (bei 2-dim. Feldern)
  und SML3_freefeldaddr()
  - wenn die Adresse des jeweiligen Elements an die Deallokationsfunktion uebergeben wird
    (bei 1-dim. Feldern)
+++ */


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

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

  // struct mit alloziertem Inhalt
  struct st_ptr {
    int i;
    char *s;
  };

  // struct ohne jede inhaltliche Allokation
  struct st_array {
    int i;
    char s[16];
  };

  // Freigabefunktion fuer Elemente von struct st_ptr
  static void elemfree(void *v) {
    struct st_ptr *tp = (struct st_ptr *)v;
    if (tp == NULL) { return; }
    if (tp->s != NULL) { free(tp->s); }  // Stringpointer freigeben
  }

  // 1.Beispiel: Feld-Element ist "struct st_array"
  static void bsp1(void) {
    struct SML3_dynfeld df;
    struct st_array *tp, t;
    int i1, len;

    SML3_dynfeld_init(&df, sizeof(t), 4);  // Allokations-Blockgroesse = 4 Elemente

    for (i1 = 0; i1 < 7; i1++) {  // 7 Elemente hinzufugen
      t.i = i1;
      snprintf(t.s, sizeof(t.s), "Zahl=%d", i1);
      SML3_dynfeld_add(&df, &t);
    }

    tp = (struct st_array *)SML3_dynfeld_get(&df, &len);  // Feld erhalten (1-dimensional)
    for (i1 = 0; i1 < len; i1++) {  // Feld-Elemente ausgeben
      printf("Index %d: i=%d, s=<%s>\n", i1, tp[i1].i, tp[i1].s);
    }

    // erhaltenes Feld freigeben,
    // Feld-Elemente sind von "struct st_array", werden an free() per Adresse uebergeben
    // Feld-Elemente haben keine inhaltliche Allokation, daher keine Deallokationsfunktion
    SML3_freefeldaddr(tp, sizeof(t), len, NULL);  // ist hier gleich free(tp)
  }

  // 2.Beispiel: Feld-Element ist "struct st_ptr"
  static void bsp2(void) {
    struct SML3_dynfeld df;
    struct st_ptr *tp, t;
    int i1, len;

    SML3_dynfeld_init(&df, sizeof(t), 4);  // Allokations-Blockgroesse = 4 Elemente

    for (i1 = 0; i1 < 7; i1++) {  // 7 Elemente hinzufugen
      char buf[16];
      t.i = i1;
      snprintf(buf, sizeof(buf), "Zahl=%d", i1);
      t.s = strdup(buf);
      SML3_dynfeld_add(&df, &t);
    }

    tp = (struct st_ptr *)SML3_dynfeld_get(&df, &len);  // Feld erhalten (1-dimensional)
    for (i1 = 0; i1 < len; i1++) {  // Feld-Elemente ausgeben
      printf("Index %d: i=%d, s=<%s>\n", i1, tp[i1].i, tp[i1].s);
    }

    // erhaltenes Feld freigeben,
    // Feld-Elemente sind von "struct st_ptr", werden an free() per Adresse uebergeben
    // Feld-Elemente haben inhaltliche Allokation, daher Deallokationsfunktion
    SML3_freefeldaddr(tp, sizeof(t), len, elemfree);
  }

  // 3.Beispiel: Feld-Element ist Pointer auf alloziertes "struct st_array"
  static void bsp3(void) {
    struct SML3_dynfeld df;
    struct st_array **tpp, *tp;
    int i1, len;

    SML3_dynfeld_init(&df, sizeof(tp), 4);  // Allokations-Blockgroesse = 4 Elemente

    for (i1 = 0; i1 < 7; i1++) {  // 7 Elemente hinzufugen
      tp = malloc(sizeof(*tp));
      tp->i = i1;
      snprintf(tp->s, sizeof(tp->s), "Zahl=%d", i1);
      SML3_dynfeld_add(&df, &tp);
    }

    tpp = (struct st_array **)SML3_dynfeld_get(&df, &len);  // Feld erhalten (2-dimensional)
    for (i1 = 0; i1 < len; i1++) {  // Feld-Elemente ausgeben
      printf("Index %d: i=%d, s=<%s>\n", i1, tpp[i1]->i, tpp[i1]->s);
    }

    // erhaltenes Feld freigeben,
    // Feld-Elemente sind von "struct st_array *", werden an free() direkt uebergeben
    // Feld-Elemente haben keine inhaltliche Allokation, daher keine Deallokationsfunktion
    SML3_freefeld(tpp, NULL);
  }

  // 4.Beispiel: Feld-Element ist Pointer auf alloziertes "struct st_ptr"
  static void bsp4(void) {
    struct SML3_dynfeld df;
    struct st_ptr **tpp, *tp;
    int i1, len;

    SML3_dynfeld_init(&df, sizeof(tp), 4);  // Allokations-Blockgroesse = 4 Elemente

    for (i1 = 0; i1 < 7; i1++) {  // 7 Elemente hinzufugen
      char buf[16];
      snprintf(buf, sizeof(buf), "Zahl=%d", i1);
      tp = malloc(sizeof(*tp));
      tp->i = i1;
      tp->s = strdup(buf);
      SML3_dynfeld_add(&df, &tp);
    }

    tpp = (struct st_ptr **)SML3_dynfeld_get(&df, &len);  // Feld erhalten (2-dimensional)
    for (i1 = 0; i1 < len; i1++) {  // Feld-Elemente ausgeben
      printf("Index %d: i=%d, s=<%s>\n", i1, tpp[i1]->i, tpp[i1]->s);
    }

    // erhaltenes Feld freigeben,
    // Feld-Elemente sind von "struct st_ptr *", werden an free() direkt uebergeben
    // Feld-Elemente haben inhaltliche Allokation, daher Deallokationsfunktion
    SML3_freefeld(tpp, elemfree);
  }

  int main(void) {
    printf("\n1. Beispiel:\n"); bsp1();
    printf("\n2. Beispiel:\n"); bsp2();
    printf("\n3. Beispiel:\n"); bsp3();
    printf("\n4. Beispiel:\n"); bsp4();
    exit(0);
  }

+++ */


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

#include <sys/types.h>

struct SML3_dynfeld {  /* dynamisches Feld */
  void *feld;       /* Feldpointer */
  size_t size;      /* Elementgroesse */
  size_t max, mom, block;  /* fuer Allokation */
};

/* Initialisierer fuer dyn.Feld: P1 ist Elementgroesse */
#define SML3_DYNFELD_INITIALIZER(P1)  { NULL, (P1), 0, 0, 16 }


/** 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
 */
extern void SML3_dynfeld_init(struct SML3_dynfeld *, size_t, size_t);


/** SML3_dynfeld_add [thread-sicher]:
 * fuegt ein Element dem dyn.Feld hinzu
 * 1.Arg: Adresse eines dyn.Feldes
 * 2.Arg: Adresse auf hinzuzufuegendes Element
 */
extern void SML3_dynfeld_add(struct SML3_dynfeld *, void *);


/** 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)
 */
extern void * SML3_dynfeld_get(struct SML3_dynfeld *, int *);


/** 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
 */
extern void SML3_freefeld(void *, void (*)(void *));


/** 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
 */
extern void SML3_freefeldaddr(void *, size_t, int, void (*)(void *));

#endif /* SML3__DNYFELD_H_ */
