summaryrefslogtreecommitdiff
path: root/csv-test.c
diff options
context:
space:
mode:
authorThomas Schmucker <ts@its1.de>2020-06-18 16:05:54 +0200
committerThomas Schmucker <ts@its1.de>2020-06-18 16:05:54 +0200
commit2caae8f8c31fd439828b2183fc852a765e0f2ee1 (patch)
tree5e24c816dcbf4a38a6bb9412bd5c2e929f18c6d8 /csv-test.c
parentb576c50666fec47ee6c95c7b8d31144528635c37 (diff)
downloadlibcsv-2caae8f8c31fd439828b2183fc852a765e0f2ee1.tar.gz
libcsv-2caae8f8c31fd439828b2183fc852a765e0f2ee1.tar.bz2
libcsv-2caae8f8c31fd439828b2183fc852a765e0f2ee1.zip
Testprogramme und Beispiele hinzugefügt
Diffstat (limited to 'csv-test.c')
-rw-r--r--csv-test.c491
1 files changed, 54 insertions, 437 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 @@
1#include <stdio.h> 1#include <stdio.h>
2#include <stdlib.h> 2#include <stdlib.h>
3#include <string.h>
4#include <time.h>
5#include <stdbool.h>
6#include <setjmp.h> 3#include <setjmp.h>
7#include <assert.h>
8#include <signal.h> 4#include <signal.h>
5#include <time.h>
9 6
10#include "csv.h" 7#include "csv.h"
11 8
12#define INLINE static inline 9static int
13 10test_exception(FILE *f)
14/* === CSV-MEMORY Interface === */
15
16static void *
17csv_mem_allocate(size_t n, size_t sz, void *cb_arg)
18{ 11{
19 (void) cb_arg; 12 int line = 0;
20 13 csv_t csv[1];
21 return calloc(n, sz);
22}
23 14
24static void * 15 csv_init(csv);
25csv_mem_reallocate(void *ptr, size_t n, size_t sz, void *cb_arg) 16 for ( int n; (n = csv_read(csv, f)) != -1; ) {
26{ 17 if (++line == 500000) csv_field(csv, -1);
27 (void) cb_arg; 18 }
19 csv_free(csv);
28 20
29 return reallocarray(ptr, n, sz); 21 return line;
30} 22}
31 23
32static void 24static void
33csv_mem_free(void *ptr, void *cb_arg) 25my_signal_handler(int sig)
34{
35 (void) cb_arg;
36
37 free(ptr);
38}
39
40/* === CSV-OPTIONS Interface === */
41
42const csv_options_t csv_default_options = {
43 .quote_symbol = '"',
44 .sep_symbol = ',',
45 .cb_error = NULL,
46 .cb_allocate = csv_mem_allocate,
47 .cb_reallocate = csv_mem_reallocate,
48 .cb_free = csv_mem_free
49};
50
51/* === CSV-ERROR Interface === */
52
53INLINE void
54csv_fatal_error(const char *msg, const csv_options_t * const csv_options)
55{ 26{
56 if ( csv_options->cb_error != NULL ) { 27 if ( sig == SIGABRT ) {
57 (*csv_options->cb_error)(msg, csv_options->cb_arg); 28 fprintf(stderr, "my_signal_handler!\n");
29 exit(1);
58 } 30 }
59
60 fprintf(stderr, "fatal error: %s\n", msg);
61 abort();
62} 31}
63 32
64/* === CSV-STRING Interface === */ 33static int
65 34test_exception_with_signal(FILE *f)
66INLINE void
67csv_string_init(csv_string_t *csv_string)
68{ 35{
69 assert(csv_string != NULL); 36 void (*old_handler)(int) = signal(SIGABRT, my_signal_handler);
70
71 csv_string->str = NULL;
72 csv_string->cap = 0;
73 csv_string->pos = 0;
74}
75
76INLINE void
77csv_string_reset(csv_string_t *csv_string)
78{
79 assert(csv_string != NULL);
80
81 csv_string->pos = 0;
82}
83 37
84INLINE bool 38 int line = 0;
85csv_string_empty(csv_string_t *csv_string) 39 csv_t csv[1];
86{
87 assert(csv_string != NULL);
88
89 return ( csv_string->pos == 0 ) ? true : false;
90}
91
92INLINE void
93csv_string_append(csv_string_t *csv_string, char ch, const csv_options_t * const csv_options)
94{
95 assert(csv_string != NULL);
96 assert(csv_options != NULL);
97
98 static const int INITIAL_CAP = 16;
99
100 if ( csv_string->pos == csv_string->cap ) { /* grow if needed */
101 if ( csv_string->str == NULL ) { /* first call? */
102 if ( (csv_string->str = csv_options->cb_allocate(INITIAL_CAP, 1, csv_options->cb_arg)) == NULL ) {
103 csv_fatal_error("out of memory...", csv_options);
104 return;
105 }
106 40
107 csv_string->cap = INITIAL_CAP; 41 csv_init(csv);
108 } 42 for ( int n; (n = csv_read(csv, f)) != -1; ) {
109 else { /* subsequent call */ 43 if (++line == 500000) csv_field(csv, -1);
110 int cap = (csv_string->cap * 3) / 2; /* *= 1.5 */
111 char *str = csv_options->cb_reallocate(csv_string->str, cap, 1, csv_options->cb_arg);
112 if ( str == NULL ) {
113 csv_fatal_error("out of memory...", csv_options);
114 return;
115 }
116 csv_string->str = str;
117 csv_string->cap = cap;
118 }
119 } 44 }
45 csv_free(csv);
120 46
121 csv_string->str[csv_string->pos++] = ch; /* append char */ 47 signal(SIGABRT, old_handler);
122}
123
124INLINE void
125csv_string_free(csv_string_t *csv_string, const csv_options_t * const csv_options)
126{
127 assert(csv_string != NULL);
128 assert(csv_options != NULL);
129
130 csv_options->cb_free(csv_string->str, csv_options->cb_arg);
131 48
132 /* call *_init() for sane default values; prevent possible double-free */ 49 return line;
133 csv_string_init(csv_string);
134} 50}
135 51
136/* === CSV-FIELD Interface === */ 52static void
137 53my_error_handler(const char *msg, void *env)
138INLINE void
139csv_field_init(csv_field_t *csv_field)
140{ 54{
141 assert(csv_field != NULL); 55 fprintf(stderr, "my_error_handler: %s\n", msg);
142 56 longjmp(*((jmp_buf *) env), 1);
143 csv_field->fields = NULL;
144 csv_field->cap = 0;
145 csv_field->pos = 0;
146} 57}
147 58
148INLINE void 59static int
149csv_field_reset(csv_field_t *csv_field) 60test_exception_longjmp(FILE *f)
150{ 61{
151 assert(csv_field != NULL); 62 /* start with default options */
152 63 csv_options_t csv_options = csv_default_options;
153 csv_field->pos = 0;
154}
155 64
156INLINE void 65 /* setup error handler */
157csv_field_append(csv_field_t *csv_field, int idx, const csv_options_t * const csv_options) 66 jmp_buf env;
158{ 67 csv_options.cb_error = my_error_handler;
159 assert(csv_field != NULL); 68 csv_options.cb_arg = &env;
160 assert(csv_options != NULL);
161 69
162 static const size_t INITIAL_CAP = 16; 70 /* setup csv-Object with custom options */
71 csv_t csv[1];
72 csv_init_opt(csv, &csv_options);
163 73
164 if ( csv_field->pos == csv_field->cap ) { /* grow if needed */ 74 if ( setjmp(env) == 0 ) {
165 if ( csv_field->fields == NULL ) { 75 int line = 0;
166 if ( (csv_field->fields = csv_options->cb_allocate(INITIAL_CAP, sizeof(csv_field->fields[0]), csv_options->cb_arg)) == NULL ) {
167 csv_fatal_error("out of memory", csv_options);
168 return;
169 }
170 76
171 csv_field->cap = INITIAL_CAP; 77 for ( int n; (n = csv_read(csv, f)) != -1; ) {
172 } 78 // process data...
173 else { 79 if (++line == 500000) csv_field(csv, -1);
174 int cap = (csv_field->cap * 3) / 2; /* *= 1.5 */
175 int *fields = csv_options->cb_reallocate(csv_field->fields, cap, sizeof(csv_field->fields[0]), csv_options->cb_arg);
176 if ( fields == NULL ) {
177 csv_fatal_error("out of memory", csv_options);
178 return;
179 }
180 csv_field->fields = fields;
181 csv_field->cap = cap;
182 } 80 }
183 } 81 csv_free(csv);
184 82 return line;
185 csv_field->fields[csv_field->pos++] = idx; /* append idx */
186}
187
188INLINE void
189csv_field_free(csv_field_t *csv_field, const csv_options_t * const csv_options)
190{
191 assert(csv_field != NULL);
192 assert(csv_options != NULL);
193
194 csv_options->cb_free(csv_field->fields, csv_options->cb_arg);
195
196 csv_field_init(csv_field);
197}
198
199/* === CSV Interface === */
200
201void
202csv_init(csv_t *csv)
203{
204 assert(csv != NULL);
205
206 csv_init_opt(csv, NULL);
207}
208
209void
210csv_init_opt(csv_t *csv, const csv_options_t * const csv_options)
211{
212 assert(csv != NULL);
213
214 if ( csv_options != NULL ) {
215 csv->csv_options = csv_options;
216 } 83 }
217 else { 84 else {
218 csv->csv_options = &csv_default_options; 85 csv_free(csv);
219 } 86 return -1;
220
221 csv_string_init(&csv->csv_string);
222 csv_field_init(&csv->csv_field);
223}
224
225void
226csv_free(csv_t *csv)
227{
228 assert(csv != NULL);
229
230 csv_string_free(&csv->csv_string, csv->csv_options);
231 csv_field_free(&csv->csv_field, csv->csv_options);
232}
233
234int
235csv_nfields(csv_t *csv)
236{
237 assert(csv != NULL);
238
239 if ( csv->csv_string.pos != 0 ) {
240 return csv->csv_field.pos;
241 }
242 return 0;
243}
244
245const char *
246csv_field(csv_t *csv, int idx)
247{
248 assert(csv != NULL);
249 assert(idx >= 0 && idx < csv->csv_field.pos);
250
251 if ( idx < 0 || idx >= csv->csv_field.pos ) {
252 csv_fatal_error("range error", csv->csv_options);
253 }
254
255 return &csv->csv_string.str[csv->csv_field.fields[idx]];
256}
257
258int
259csv_read(csv_t *csv, FILE *in)
260{
261 assert(csv != NULL);
262 assert(in != NULL);
263
264 enum {
265 STATE_START_FIELD,
266 STATE_QUOTED_FIELD,
267 STATE_SIMPLE_FIELD,
268 STATE_END_FIELD,
269 STATE_END_LINE,
270 STATE_END_FILE,
271 };
272
273 register const int QUOTE = csv->csv_options->quote_symbol;
274 register const int SEP = csv->csv_options->sep_symbol;
275
276 csv_string_reset(&csv->csv_string);
277 csv_field_reset(&csv->csv_field);
278
279 for ( int state = STATE_START_FIELD; ; ) {
280 int ch;
281
282 switch ( state ) {
283 case STATE_START_FIELD:
284 csv_field_append(&csv->csv_field, csv->csv_string.pos, csv->csv_options);
285
286 ch = getc(in);
287 if ( ch == EOF ) {
288 state = STATE_END_FILE;
289 }
290 else if ( ch == '\r' ) { /* Teste auf CR.. */
291 ch = getc(in);
292 if ( ch != '\n' ) { /* ... LF */
293 ungetc(ch, in);
294 }
295 state = STATE_END_LINE;
296 }
297 else if ( ch == '\n' ) {
298 state = STATE_END_LINE;
299 }
300 else if ( ch == SEP ) {
301 state = STATE_END_FIELD;
302 }
303 else if ( ch == QUOTE ) {
304 state = STATE_QUOTED_FIELD;
305 }
306 else {
307 csv_string_append(&csv->csv_string, ch, csv->csv_options);
308 state = STATE_SIMPLE_FIELD;
309 }
310 break;
311
312 case STATE_QUOTED_FIELD:
313 do {
314 ch = getc(in);
315 if ( ch == EOF ) {
316 state = STATE_END_FILE;
317 }
318 else if ( ch == QUOTE ) {
319 ch = getc(in);
320 if ( ch == EOF ) {
321 state = STATE_END_FILE;
322 }
323 else if ( ch == QUOTE ) {
324 csv_string_append(&csv->csv_string, QUOTE, csv->csv_options);
325 }
326 else if ( ch == SEP ) {
327 state = STATE_END_FIELD;
328 }
329 else if ( ch == '\r' ) {
330 ch = getc(in);
331 if ( ch != '\n' ) {
332 ungetc(ch, in);
333 }
334 state = STATE_END_LINE;
335 }
336 else if ( ch == '\n' ) {
337 state = STATE_END_LINE;
338 }
339 else {
340 csv_string_append(&csv->csv_string, QUOTE, csv->csv_options);
341 ungetc(ch, in); /* zuviel gelesenes Zeichen zurückstellen */
342 }
343 }
344 else {
345 csv_string_append(&csv->csv_string, ch, csv->csv_options);
346 }
347 } while ( state == STATE_QUOTED_FIELD );
348 break;
349
350 case STATE_SIMPLE_FIELD:
351 do {
352 ch = getc(in);
353 if ( ch == EOF ) {
354 state = STATE_END_FILE;
355 }
356 else if ( ch == SEP ) {
357 state = STATE_END_FIELD;
358 }
359 else if ( ch == '\r' ) {
360 ch = getc(in);
361 if ( ch != '\n' ) {
362 ungetc(ch, in);
363 }
364 state = STATE_END_LINE;
365 }
366 else if ( ch == '\n' ) {
367 state = STATE_END_LINE;
368 }
369 else {
370 csv_string_append(&csv->csv_string, ch, csv->csv_options);
371 }
372 } while ( state == STATE_SIMPLE_FIELD );
373 break;
374
375 case STATE_END_FIELD:
376 csv_string_append(&csv->csv_string, '\0', csv->csv_options);
377 state = STATE_START_FIELD;
378 break;
379
380 case STATE_END_LINE:
381 csv_string_append(&csv->csv_string, '\0', csv->csv_options);
382 return csv->csv_field.pos;
383
384 case STATE_END_FILE:
385 if ( csv_string_empty(&csv->csv_string) ) {
386 return -1; /* EOF reached */
387 }
388
389 /* EOF wurde gelesen, allerdings sind Daten zur Vearbeitung vorhanden! */
390 /* => aktuelles Feld abschließen... */
391 csv_string_append(&csv->csv_string, '\0', csv->csv_options);
392
393 /* ... und die Anzahl der Felder zurückliefern! */
394 return csv->csv_field.pos;
395
396 default:
397 assert(!"this should never be happen...");
398 break;
399 }
400 }
401 /* NOT REACHED */
402}
403
404static void
405csv_write_field(FILE *out, const char *field, const csv_options_t * const csv_options)
406{
407 assert(csv_options != NULL);
408 assert(field != NULL);
409 assert(out != NULL);
410
411 const char QUOTE = csv_options->quote_symbol;
412
413 if ( *field != '\0' ) {
414 putc(QUOTE, out);
415
416 for ( ; *field != '\0'; ++field ) {
417 if ( *field == QUOTE ) {
418 putc(QUOTE, out);
419 putc(QUOTE, out);
420 }
421 else {
422 putc(*field, out);
423 }
424 }
425
426 putc(QUOTE, out);
427 }
428}
429
430void
431csv_write(FILE *out, int n, const char *fields[], const csv_options_t * const csv_options)
432{
433 assert(csv_options != NULL);
434 assert(fields != NULL);
435 assert(out != NULL);
436
437 const char SEP = csv_options->quote_symbol;
438
439 /* process first field */
440 if ( 0 != n && fields[0] != NULL ) {
441 csv_write_field(out, fields[0], csv_options);
442
443 /* process next fields */
444 for ( int i = 1; i != n && fields[i] != NULL; ++i ) {
445 putc(SEP, out);
446 csv_write_field(out, fields[i], csv_options);
447 }
448 } 87 }
449 fprintf(out, "\r\n");
450}
451
452jmp_buf env;
453
454static void sig_handler(int sig)
455{
456 (void) sig;
457 longjmp(env, 1);
458} 88}
459 89
460int 90int
461main(void) 91main(void)
462{ 92{
463 csv_t csv[1];
464 int n, line = 0;
465
466 signal(SIGABRT, sig_handler);
467
468 FILE *in = fopen("500000 Records.csv", "r"); 93 FILE *in = fopen("500000 Records.csv", "r");
469 if ( in == NULL ) { 94 if ( in == NULL ) {
470 fprintf(stderr, "failed to open testfile...\n"); 95 fprintf(stderr, "failed to open testfile...\n");
@@ -473,23 +98,15 @@ main(void)
473 98
474 clock_t start = clock(); 99 clock_t start = clock();
475 100
476 csv_init(csv); 101 (void) test_exception;
477 if ( setjmp(env) == 0 ) { 102 (void) test_exception_with_signal;
478 while ( (n = csv_read(csv, in)) != -1 ) { 103 int lines = test_exception_longjmp(in);
479 //if ( line == 500000 ) csv_field(csv, -1);
480 ++line;
481 }
482 }
483 else {
484 printf("Fehler beim Verarbeiten der Datei...\n");
485 }
486 csv_free(csv);
487 104
488 clock_t end = clock(); 105 clock_t end = clock();
489 106
490 fclose(in); 107 fclose(in);
491 108
492 printf("%d lines processed, duration: %.3lf sec\n", line, ((double)(end - start)) / CLOCKS_PER_SEC); 109 printf("%d lines processed, duration: %.3lf sec\n", lines, ((double)(end - start)) / CLOCKS_PER_SEC);
493 110
494 return EXIT_SUCCESS; 111 return EXIT_SUCCESS;
495} 112}