From 2caae8f8c31fd439828b2183fc852a765e0f2ee1 Mon Sep 17 00:00:00 2001 From: Thomas Schmucker Date: Thu, 18 Jun 2020 16:05:54 +0200 Subject: Testprogramme und Beispiele hinzugefügt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- csv-test.c | 491 +++++++------------------------------------------------------ makefile | 6 +- 2 files changed, 57 insertions(+), 440 deletions(-) diff --git a/csv-test.c b/csv-test.c index de92e46..a22b724 100644 --- a/csv-test.c +++ b/csv-test.c @@ -1,470 +1,95 @@ #include #include -#include -#include -#include #include -#include #include +#include #include "csv.h" -#define INLINE static inline - -/* === CSV-MEMORY Interface === */ - -static void * -csv_mem_allocate(size_t n, size_t sz, void *cb_arg) +static int +test_exception(FILE *f) { - (void) cb_arg; - - return calloc(n, sz); -} + int line = 0; + csv_t csv[1]; -static void * -csv_mem_reallocate(void *ptr, size_t n, size_t sz, void *cb_arg) -{ - (void) cb_arg; + csv_init(csv); + for ( int n; (n = csv_read(csv, f)) != -1; ) { + if (++line == 500000) csv_field(csv, -1); + } + csv_free(csv); - return reallocarray(ptr, n, sz); + return line; } static void -csv_mem_free(void *ptr, void *cb_arg) -{ - (void) cb_arg; - - free(ptr); -} - -/* === CSV-OPTIONS Interface === */ - -const csv_options_t csv_default_options = { - .quote_symbol = '"', - .sep_symbol = ',', - .cb_error = NULL, - .cb_allocate = csv_mem_allocate, - .cb_reallocate = csv_mem_reallocate, - .cb_free = csv_mem_free -}; - -/* === CSV-ERROR Interface === */ - -INLINE void -csv_fatal_error(const char *msg, const csv_options_t * const csv_options) +my_signal_handler(int sig) { - if ( csv_options->cb_error != NULL ) { - (*csv_options->cb_error)(msg, csv_options->cb_arg); + if ( sig == SIGABRT ) { + fprintf(stderr, "my_signal_handler!\n"); + exit(1); } - - fprintf(stderr, "fatal error: %s\n", msg); - abort(); } -/* === CSV-STRING Interface === */ - -INLINE void -csv_string_init(csv_string_t *csv_string) +static int +test_exception_with_signal(FILE *f) { - assert(csv_string != NULL); - - csv_string->str = NULL; - csv_string->cap = 0; - csv_string->pos = 0; -} - -INLINE void -csv_string_reset(csv_string_t *csv_string) -{ - assert(csv_string != NULL); - - csv_string->pos = 0; -} + void (*old_handler)(int) = signal(SIGABRT, my_signal_handler); -INLINE bool -csv_string_empty(csv_string_t *csv_string) -{ - assert(csv_string != NULL); - - return ( csv_string->pos == 0 ) ? true : false; -} - -INLINE void -csv_string_append(csv_string_t *csv_string, char ch, const csv_options_t * const csv_options) -{ - assert(csv_string != NULL); - assert(csv_options != NULL); - - static const int INITIAL_CAP = 16; - - if ( csv_string->pos == csv_string->cap ) { /* grow if needed */ - if ( csv_string->str == NULL ) { /* first call? */ - if ( (csv_string->str = csv_options->cb_allocate(INITIAL_CAP, 1, csv_options->cb_arg)) == NULL ) { - csv_fatal_error("out of memory...", csv_options); - return; - } + int line = 0; + csv_t csv[1]; - csv_string->cap = INITIAL_CAP; - } - else { /* subsequent call */ - int cap = (csv_string->cap * 3) / 2; /* *= 1.5 */ - char *str = csv_options->cb_reallocate(csv_string->str, cap, 1, csv_options->cb_arg); - if ( str == NULL ) { - csv_fatal_error("out of memory...", csv_options); - return; - } - csv_string->str = str; - csv_string->cap = cap; - } + csv_init(csv); + for ( int n; (n = csv_read(csv, f)) != -1; ) { + if (++line == 500000) csv_field(csv, -1); } + csv_free(csv); - csv_string->str[csv_string->pos++] = ch; /* append char */ -} - -INLINE void -csv_string_free(csv_string_t *csv_string, const csv_options_t * const csv_options) -{ - assert(csv_string != NULL); - assert(csv_options != NULL); - - csv_options->cb_free(csv_string->str, csv_options->cb_arg); + signal(SIGABRT, old_handler); - /* call *_init() for sane default values; prevent possible double-free */ - csv_string_init(csv_string); + return line; } -/* === CSV-FIELD Interface === */ - -INLINE void -csv_field_init(csv_field_t *csv_field) +static void +my_error_handler(const char *msg, void *env) { - assert(csv_field != NULL); - - csv_field->fields = NULL; - csv_field->cap = 0; - csv_field->pos = 0; + fprintf(stderr, "my_error_handler: %s\n", msg); + longjmp(*((jmp_buf *) env), 1); } -INLINE void -csv_field_reset(csv_field_t *csv_field) +static int +test_exception_longjmp(FILE *f) { - assert(csv_field != NULL); - - csv_field->pos = 0; -} + /* start with default options */ + csv_options_t csv_options = csv_default_options; -INLINE void -csv_field_append(csv_field_t *csv_field, int idx, const csv_options_t * const csv_options) -{ - assert(csv_field != NULL); - assert(csv_options != NULL); + /* setup error handler */ + jmp_buf env; + csv_options.cb_error = my_error_handler; + csv_options.cb_arg = &env; - static const size_t INITIAL_CAP = 16; + /* setup csv-Object with custom options */ + csv_t csv[1]; + csv_init_opt(csv, &csv_options); - if ( csv_field->pos == csv_field->cap ) { /* grow if needed */ - if ( csv_field->fields == NULL ) { - if ( (csv_field->fields = csv_options->cb_allocate(INITIAL_CAP, sizeof(csv_field->fields[0]), csv_options->cb_arg)) == NULL ) { - csv_fatal_error("out of memory", csv_options); - return; - } + if ( setjmp(env) == 0 ) { + int line = 0; - csv_field->cap = INITIAL_CAP; - } - else { - int cap = (csv_field->cap * 3) / 2; /* *= 1.5 */ - int *fields = csv_options->cb_reallocate(csv_field->fields, cap, sizeof(csv_field->fields[0]), csv_options->cb_arg); - if ( fields == NULL ) { - csv_fatal_error("out of memory", csv_options); - return; - } - csv_field->fields = fields; - csv_field->cap = cap; + for ( int n; (n = csv_read(csv, f)) != -1; ) { + // process data... + if (++line == 500000) csv_field(csv, -1); } - } - - csv_field->fields[csv_field->pos++] = idx; /* append idx */ -} - -INLINE void -csv_field_free(csv_field_t *csv_field, const csv_options_t * const csv_options) -{ - assert(csv_field != NULL); - assert(csv_options != NULL); - - csv_options->cb_free(csv_field->fields, csv_options->cb_arg); - - csv_field_init(csv_field); -} - -/* === CSV Interface === */ - -void -csv_init(csv_t *csv) -{ - assert(csv != NULL); - - csv_init_opt(csv, NULL); -} - -void -csv_init_opt(csv_t *csv, const csv_options_t * const csv_options) -{ - assert(csv != NULL); - - if ( csv_options != NULL ) { - csv->csv_options = csv_options; + csv_free(csv); + return line; } else { - csv->csv_options = &csv_default_options; - } - - csv_string_init(&csv->csv_string); - csv_field_init(&csv->csv_field); -} - -void -csv_free(csv_t *csv) -{ - assert(csv != NULL); - - csv_string_free(&csv->csv_string, csv->csv_options); - csv_field_free(&csv->csv_field, csv->csv_options); -} - -int -csv_nfields(csv_t *csv) -{ - assert(csv != NULL); - - if ( csv->csv_string.pos != 0 ) { - return csv->csv_field.pos; - } - return 0; -} - -const char * -csv_field(csv_t *csv, int idx) -{ - assert(csv != NULL); - assert(idx >= 0 && idx < csv->csv_field.pos); - - if ( idx < 0 || idx >= csv->csv_field.pos ) { - csv_fatal_error("range error", csv->csv_options); - } - - return &csv->csv_string.str[csv->csv_field.fields[idx]]; -} - -int -csv_read(csv_t *csv, FILE *in) -{ - assert(csv != NULL); - assert(in != NULL); - - enum { - STATE_START_FIELD, - STATE_QUOTED_FIELD, - STATE_SIMPLE_FIELD, - STATE_END_FIELD, - STATE_END_LINE, - STATE_END_FILE, - }; - - register const int QUOTE = csv->csv_options->quote_symbol; - register const int SEP = csv->csv_options->sep_symbol; - - csv_string_reset(&csv->csv_string); - csv_field_reset(&csv->csv_field); - - for ( int state = STATE_START_FIELD; ; ) { - int ch; - - switch ( state ) { - case STATE_START_FIELD: - csv_field_append(&csv->csv_field, csv->csv_string.pos, csv->csv_options); - - ch = getc(in); - if ( ch == EOF ) { - state = STATE_END_FILE; - } - else if ( ch == '\r' ) { /* Teste auf CR.. */ - ch = getc(in); - if ( ch != '\n' ) { /* ... LF */ - ungetc(ch, in); - } - state = STATE_END_LINE; - } - else if ( ch == '\n' ) { - state = STATE_END_LINE; - } - else if ( ch == SEP ) { - state = STATE_END_FIELD; - } - else if ( ch == QUOTE ) { - state = STATE_QUOTED_FIELD; - } - else { - csv_string_append(&csv->csv_string, ch, csv->csv_options); - state = STATE_SIMPLE_FIELD; - } - break; - - case STATE_QUOTED_FIELD: - do { - ch = getc(in); - if ( ch == EOF ) { - state = STATE_END_FILE; - } - else if ( ch == QUOTE ) { - ch = getc(in); - if ( ch == EOF ) { - state = STATE_END_FILE; - } - else if ( ch == QUOTE ) { - csv_string_append(&csv->csv_string, QUOTE, csv->csv_options); - } - else if ( ch == SEP ) { - state = STATE_END_FIELD; - } - else if ( ch == '\r' ) { - ch = getc(in); - if ( ch != '\n' ) { - ungetc(ch, in); - } - state = STATE_END_LINE; - } - else if ( ch == '\n' ) { - state = STATE_END_LINE; - } - else { - csv_string_append(&csv->csv_string, QUOTE, csv->csv_options); - ungetc(ch, in); /* zuviel gelesenes Zeichen zurückstellen */ - } - } - else { - csv_string_append(&csv->csv_string, ch, csv->csv_options); - } - } while ( state == STATE_QUOTED_FIELD ); - break; - - case STATE_SIMPLE_FIELD: - do { - ch = getc(in); - if ( ch == EOF ) { - state = STATE_END_FILE; - } - else if ( ch == SEP ) { - state = STATE_END_FIELD; - } - else if ( ch == '\r' ) { - ch = getc(in); - if ( ch != '\n' ) { - ungetc(ch, in); - } - state = STATE_END_LINE; - } - else if ( ch == '\n' ) { - state = STATE_END_LINE; - } - else { - csv_string_append(&csv->csv_string, ch, csv->csv_options); - } - } while ( state == STATE_SIMPLE_FIELD ); - break; - - case STATE_END_FIELD: - csv_string_append(&csv->csv_string, '\0', csv->csv_options); - state = STATE_START_FIELD; - break; - - case STATE_END_LINE: - csv_string_append(&csv->csv_string, '\0', csv->csv_options); - return csv->csv_field.pos; - - case STATE_END_FILE: - if ( csv_string_empty(&csv->csv_string) ) { - return -1; /* EOF reached */ - } - - /* EOF wurde gelesen, allerdings sind Daten zur Vearbeitung vorhanden! */ - /* => aktuelles Feld abschließen... */ - csv_string_append(&csv->csv_string, '\0', csv->csv_options); - - /* ... und die Anzahl der Felder zurückliefern! */ - return csv->csv_field.pos; - - default: - assert(!"this should never be happen..."); - break; - } - } - /* NOT REACHED */ -} - -static void -csv_write_field(FILE *out, const char *field, const csv_options_t * const csv_options) -{ - assert(csv_options != NULL); - assert(field != NULL); - assert(out != NULL); - - const char QUOTE = csv_options->quote_symbol; - - if ( *field != '\0' ) { - putc(QUOTE, out); - - for ( ; *field != '\0'; ++field ) { - if ( *field == QUOTE ) { - putc(QUOTE, out); - putc(QUOTE, out); - } - else { - putc(*field, out); - } - } - - putc(QUOTE, out); - } -} - -void -csv_write(FILE *out, int n, const char *fields[], const csv_options_t * const csv_options) -{ - assert(csv_options != NULL); - assert(fields != NULL); - assert(out != NULL); - - const char SEP = csv_options->quote_symbol; - - /* process first field */ - if ( 0 != n && fields[0] != NULL ) { - csv_write_field(out, fields[0], csv_options); - - /* process next fields */ - for ( int i = 1; i != n && fields[i] != NULL; ++i ) { - putc(SEP, out); - csv_write_field(out, fields[i], csv_options); - } + csv_free(csv); + return -1; } - fprintf(out, "\r\n"); -} - -jmp_buf env; - -static void sig_handler(int sig) -{ - (void) sig; - longjmp(env, 1); } int main(void) { - csv_t csv[1]; - int n, line = 0; - - signal(SIGABRT, sig_handler); - FILE *in = fopen("500000 Records.csv", "r"); if ( in == NULL ) { fprintf(stderr, "failed to open testfile...\n"); @@ -473,23 +98,15 @@ main(void) clock_t start = clock(); - csv_init(csv); - if ( setjmp(env) == 0 ) { - while ( (n = csv_read(csv, in)) != -1 ) { - //if ( line == 500000 ) csv_field(csv, -1); - ++line; - } - } - else { - printf("Fehler beim Verarbeiten der Datei...\n"); - } - csv_free(csv); + (void) test_exception; + (void) test_exception_with_signal; + int lines = test_exception_longjmp(in); clock_t end = clock(); fclose(in); - printf("%d lines processed, duration: %.3lf sec\n", line, ((double)(end - start)) / CLOCKS_PER_SEC); + printf("%d lines processed, duration: %.3lf sec\n", lines, ((double)(end - start)) / CLOCKS_PER_SEC); return EXIT_SUCCESS; } diff --git a/makefile b/makefile index eb96805..eaedfb0 100644 --- a/makefile +++ b/makefile @@ -1,10 +1,10 @@ all: csv-test seq-read CC=cc -CFLAGS=-Wall -Wextra -pedantic -std=c11 -O2 +CFLAGS=-Wall -Wextra -pedantic -std=c11 -O3 -DNDEBUG -csv-test: csv-test.c - $(CC) $(CFLAGS) -o $@ $< +csv-test: csv-test.c csv.c csv.h + $(CC) $(CFLAGS) -o $@ csv-test.c csv.c seq-read: seq-read.c $(CC) $(CFLAGS) -o $@ $< -- cgit v1.3