diff options
| -rw-r--r-- | config.mk | 2 | ||||
| -rw-r--r-- | csv-perf.c | 2 | ||||
| -rw-r--r-- | csv-test.c | 38 | ||||
| -rw-r--r-- | csv.c | 28 | ||||
| -rw-r--r-- | csv.h | 12 |
5 files changed, 53 insertions, 29 deletions
| @@ -1,3 +1,3 @@ | |||
| 1 | CC=cc | 1 | CC=cc |
| 2 | CFLAGS=-Wall -Wextra -pedantic -std=c99 -O2 | 2 | CFLAGS=-Wall -Wextra -Wsign-compare -Wsign-conversion -pedantic -std=c99 -O2 |
| 3 | CFLAGS-TEST=$(CFLAGS) -fsanitize=address -ggdb -fprofile-instr-generate -fcoverage-mapping | 3 | CFLAGS-TEST=$(CFLAGS) -fsanitize=address -ggdb -fprofile-instr-generate -fcoverage-mapping |
| @@ -13,7 +13,7 @@ test(FILE *f) | |||
| 13 | int line = 0; | 13 | int line = 0; |
| 14 | csv_t csv = { 0 }; | 14 | csv_t csv = { 0 }; |
| 15 | 15 | ||
| 16 | while ( csv_read(&csv, f) != EOF ) { | 16 | while ( csv_read(&csv, f) ) { |
| 17 | ++line; | 17 | ++line; |
| 18 | } | 18 | } |
| 19 | 19 | ||
| @@ -56,7 +56,7 @@ test_line_endings1(void) | |||
| 56 | assert(strcmp(csv_field(&csv, 1), "E") == 0); | 56 | assert(strcmp(csv_field(&csv, 1), "E") == 0); |
| 57 | assert(strcmp(csv_field(&csv, 2), "F") == 0); | 57 | assert(strcmp(csv_field(&csv, 2), "F") == 0); |
| 58 | 58 | ||
| 59 | assert(csv_read(&csv, f) == EOF); | 59 | assert(csv_read(&csv, f) == 0); |
| 60 | 60 | ||
| 61 | FMEMCLOSE(f); | 61 | FMEMCLOSE(f); |
| 62 | } | 62 | } |
| @@ -84,7 +84,7 @@ test_line_endings2(void) | |||
| 84 | assert(strcmp(csv_field(&csv, 1), "E") == 0); | 84 | assert(strcmp(csv_field(&csv, 1), "E") == 0); |
| 85 | assert(strcmp(csv_field(&csv, 2), "F") == 0); | 85 | assert(strcmp(csv_field(&csv, 2), "F") == 0); |
| 86 | 86 | ||
| 87 | assert(csv_read(&csv, f) == EOF); | 87 | assert(csv_read(&csv, f) == 0); |
| 88 | 88 | ||
| 89 | FMEMCLOSE(f); | 89 | FMEMCLOSE(f); |
| 90 | } | 90 | } |
| @@ -233,7 +233,7 @@ test_empty_fields(void) | |||
| 233 | assert(strcmp(csv_field(&csv, 2), "") == 0); | 233 | assert(strcmp(csv_field(&csv, 2), "") == 0); |
| 234 | 234 | ||
| 235 | // EOF | 235 | // EOF |
| 236 | assert(csv_read(&csv, f) == EOF); | 236 | assert(csv_read(&csv, f) == 0); |
| 237 | 237 | ||
| 238 | FMEMCLOSE(f); | 238 | FMEMCLOSE(f); |
| 239 | } | 239 | } |
| @@ -382,7 +382,7 @@ test_simple_fields(void) | |||
| 382 | assert(strcmp(csv_field(&csv, 2), "C") == 0); | 382 | assert(strcmp(csv_field(&csv, 2), "C") == 0); |
| 383 | 383 | ||
| 384 | // EOF | 384 | // EOF |
| 385 | assert(csv_read(&csv, f) == EOF); | 385 | assert(csv_read(&csv, f) == 0); |
| 386 | 386 | ||
| 387 | FMEMCLOSE(f); | 387 | FMEMCLOSE(f); |
| 388 | } | 388 | } |
| @@ -405,7 +405,7 @@ test_quoted_fields(void) | |||
| 405 | assert(strcmp(csv_field(&csv, 3), "foo \"baz\" bar") == 0); | 405 | assert(strcmp(csv_field(&csv, 3), "foo \"baz\" bar") == 0); |
| 406 | assert(strcmp(csv_field(&csv, 4), "foo \"baz\", bar") == 0); | 406 | assert(strcmp(csv_field(&csv, 4), "foo \"baz\", bar") == 0); |
| 407 | 407 | ||
| 408 | assert(csv_read(&csv, f) == EOF); | 408 | assert(csv_read(&csv, f) == 0); |
| 409 | 409 | ||
| 410 | FMEMCLOSE(f); | 410 | FMEMCLOSE(f); |
| 411 | } | 411 | } |
| @@ -424,7 +424,7 @@ test_wrong_quoted_field(void) | |||
| 424 | assert(csv_nfields(&csv) == 1); | 424 | assert(csv_nfields(&csv) == 1); |
| 425 | assert(strcmp(csv_field(&csv, 0), "foo") == 0); | 425 | assert(strcmp(csv_field(&csv, 0), "foo") == 0); |
| 426 | 426 | ||
| 427 | assert(csv_read(&csv, f) == EOF); | 427 | assert(csv_read(&csv, f) == 0); |
| 428 | 428 | ||
| 429 | FMEMCLOSE(f); | 429 | FMEMCLOSE(f); |
| 430 | } | 430 | } |
| @@ -488,7 +488,7 @@ test_read_error(void) | |||
| 488 | 488 | ||
| 489 | switch ( setjmp(env) ) { | 489 | switch ( setjmp(env) ) { |
| 490 | case CSV_ERR_OK: | 490 | case CSV_ERR_OK: |
| 491 | assert(csv_read(&csv, f) == EOF); | 491 | assert(csv_read(&csv, f) == 0); |
| 492 | break; | 492 | break; |
| 493 | 493 | ||
| 494 | case CSV_ERR_IO_READ: | 494 | case CSV_ERR_IO_READ: |
| @@ -580,9 +580,33 @@ test_allocation_error1(void) | |||
| 580 | FMEMCLOSE(f); | 580 | FMEMCLOSE(f); |
| 581 | } | 581 | } |
| 582 | 582 | ||
| 583 | void | ||
| 584 | show_version(void) | ||
| 585 | { | ||
| 586 | const char *version = csv_version; | ||
| 587 | |||
| 588 | printf("Version: "); | ||
| 589 | for ( ; *version; ++version ) { | ||
| 590 | putchar(*version); | ||
| 591 | } | ||
| 592 | |||
| 593 | printf(", Build Date: "); | ||
| 594 | for ( ++version; *version; ++version ) { | ||
| 595 | putchar(*version); | ||
| 596 | } | ||
| 597 | |||
| 598 | printf(", Build Time: "); | ||
| 599 | for ( ++version; *version; ++version ) { | ||
| 600 | putchar(*version); | ||
| 601 | } | ||
| 602 | putchar('\n'); | ||
| 603 | } | ||
| 604 | |||
| 583 | int | 605 | int |
| 584 | main(void) | 606 | main(void) |
| 585 | { | 607 | { |
| 608 | show_version(); | ||
| 609 | |||
| 586 | test_empty_object(); | 610 | test_empty_object(); |
| 587 | test_line_endings1(); | 611 | test_line_endings1(); |
| 588 | test_line_endings2(); | 612 | test_line_endings2(); |
| @@ -134,7 +134,7 @@ csv_string_append(csv_string_t *csv_string, char ch, const csv_options_t *const | |||
| 134 | assert(csv_string != NULL); | 134 | assert(csv_string != NULL); |
| 135 | assert(csv_options != NULL); | 135 | assert(csv_options != NULL); |
| 136 | 136 | ||
| 137 | static const int INITIAL_CAP = 16; | 137 | static const size_t INITIAL_CAP = 16; |
| 138 | 138 | ||
| 139 | if ( csv_string->pos == csv_string->cap ) { // grow if needed | 139 | if ( csv_string->pos == csv_string->cap ) { // grow if needed |
| 140 | if ( csv_string->str == NULL ) { // first call? | 140 | if ( csv_string->str == NULL ) { // first call? |
| @@ -144,9 +144,9 @@ csv_string_append(csv_string_t *csv_string, char ch, const csv_options_t *const | |||
| 144 | } | 144 | } |
| 145 | csv_string->cap = INITIAL_CAP; | 145 | csv_string->cap = INITIAL_CAP; |
| 146 | } | 146 | } |
| 147 | else { // subsequent call | 147 | else { // subsequent call |
| 148 | int cap = (csv_string->cap * 3) / 2; // *= 1.5 | 148 | size_t cap = (csv_string->cap * 3) / 2; // *= 1.5 |
| 149 | char *str = csv_options->cb_reallocate(csv_string->str, cap, 1, csv_options->cb_memory_arg); | 149 | char * str = csv_options->cb_reallocate(csv_string->str, cap, 1, csv_options->cb_memory_arg); |
| 150 | if ( str == NULL ) { | 150 | if ( str == NULL ) { |
| 151 | csv_fatal_error(CSV_ERR_OUT_OF_MEMORY, csv_options); | 151 | csv_fatal_error(CSV_ERR_OUT_OF_MEMORY, csv_options); |
| 152 | return; | 152 | return; |
| @@ -192,7 +192,7 @@ csv_field_reset(csv_field_t *csv_field) | |||
| 192 | } | 192 | } |
| 193 | 193 | ||
| 194 | static inline void | 194 | static inline void |
| 195 | csv_field_append(csv_field_t *csv_field, int idx, const csv_options_t *const csv_options) | 195 | csv_field_append(csv_field_t *csv_field, size_t idx, const csv_options_t *const csv_options) |
| 196 | { | 196 | { |
| 197 | assert(csv_field != NULL); | 197 | assert(csv_field != NULL); |
| 198 | assert(csv_options != NULL); | 198 | assert(csv_options != NULL); |
| @@ -207,9 +207,9 @@ csv_field_append(csv_field_t *csv_field, int idx, const csv_options_t *const csv | |||
| 207 | } | 207 | } |
| 208 | csv_field->cap = INITIAL_CAP; | 208 | csv_field->cap = INITIAL_CAP; |
| 209 | } | 209 | } |
| 210 | else { // subsequent call | 210 | else { // subsequent call |
| 211 | int cap = (csv_field->cap * 3) / 2; // *= 1.5 | 211 | size_t cap = (csv_field->cap * 3) / 2; // *= 1.5 |
| 212 | int *fields = csv_options->cb_reallocate(csv_field->fields, cap, sizeof(csv_field->fields[0]), csv_options->cb_memory_arg); | 212 | size_t *fields = csv_options->cb_reallocate(csv_field->fields, cap, sizeof(csv_field->fields[0]), csv_options->cb_memory_arg); |
| 213 | if ( fields == NULL ) { | 213 | if ( fields == NULL ) { |
| 214 | csv_fatal_error(CSV_ERR_OUT_OF_MEMORY, csv_options); | 214 | csv_fatal_error(CSV_ERR_OUT_OF_MEMORY, csv_options); |
| 215 | return; | 215 | return; |
| @@ -270,7 +270,7 @@ csv_cleanup(csv_t *csv) | |||
| 270 | csv->csv_options = NULL; | 270 | csv->csv_options = NULL; |
| 271 | } | 271 | } |
| 272 | 272 | ||
| 273 | int | 273 | size_t |
| 274 | csv_nfields(const csv_t *const csv) | 274 | csv_nfields(const csv_t *const csv) |
| 275 | { | 275 | { |
| 276 | assert(csv != NULL); | 276 | assert(csv != NULL); |
| @@ -279,7 +279,7 @@ csv_nfields(const csv_t *const csv) | |||
| 279 | } | 279 | } |
| 280 | 280 | ||
| 281 | const char * | 281 | const char * |
| 282 | csv_field(const csv_t *const csv, int idx) | 282 | csv_field(const csv_t *const csv, size_t idx) |
| 283 | { | 283 | { |
| 284 | assert(csv != NULL); | 284 | assert(csv != NULL); |
| 285 | assert(idx >= 0 && idx < csv->csv_field.pos); | 285 | assert(idx >= 0 && idx < csv->csv_field.pos); |
| @@ -304,13 +304,13 @@ csv_read(csv_t *csv, FILE *in) | |||
| 304 | 304 | ||
| 305 | if ( ferror(in) ) { | 305 | if ( ferror(in) ) { |
| 306 | csv_fatal_error(CSV_ERR_IO_READ, csv->csv_options); | 306 | csv_fatal_error(CSV_ERR_IO_READ, csv->csv_options); |
| 307 | return EOF; | 307 | return 0; |
| 308 | } | 308 | } |
| 309 | 309 | ||
| 310 | // do not try to read if EOF has already been seen | 310 | // do not try to read if EOF has already been seen |
| 311 | if ( feof(in) ) { | 311 | if ( feof(in) ) { |
| 312 | csv_cleanup(csv); | 312 | csv_cleanup(csv); |
| 313 | return EOF; | 313 | return 0; |
| 314 | } | 314 | } |
| 315 | 315 | ||
| 316 | enum { | 316 | enum { |
| @@ -442,12 +442,12 @@ csv_read(csv_t *csv, FILE *in) | |||
| 442 | case STATE_END_FILE: | 442 | case STATE_END_FILE: |
| 443 | if ( ferror(in) ) { | 443 | if ( ferror(in) ) { |
| 444 | csv_fatal_error(CSV_ERR_IO_READ, csv->csv_options); | 444 | csv_fatal_error(CSV_ERR_IO_READ, csv->csv_options); |
| 445 | return EOF; | 445 | return 0; |
| 446 | } | 446 | } |
| 447 | 447 | ||
| 448 | if ( csv_string_empty(&csv->csv_string) ) { | 448 | if ( csv_string_empty(&csv->csv_string) ) { |
| 449 | csv_cleanup(csv); | 449 | csv_cleanup(csv); |
| 450 | return EOF; // EOF reached | 450 | return 0; // EOF reached |
| 451 | } | 451 | } |
| 452 | 452 | ||
| 453 | /* | 453 | /* |
| @@ -31,13 +31,13 @@ typedef struct { | |||
| 31 | extern const csv_options_t csv_default_options; | 31 | extern const csv_options_t csv_default_options; |
| 32 | 32 | ||
| 33 | typedef struct { | 33 | typedef struct { |
| 34 | char *str; | 34 | char * str; |
| 35 | int cap, pos; | 35 | size_t cap, pos; |
| 36 | } csv_string_t; | 36 | } csv_string_t; |
| 37 | 37 | ||
| 38 | typedef struct { | 38 | typedef struct { |
| 39 | int *fields; | 39 | size_t *fields; |
| 40 | int cap, pos; | 40 | size_t cap, pos; |
| 41 | } csv_field_t; | 41 | } csv_field_t; |
| 42 | 42 | ||
| 43 | typedef struct { | 43 | typedef struct { |
| @@ -60,8 +60,8 @@ void csv_cleanup(csv_t *csv); | |||
| 60 | int csv_read(csv_t *csv, FILE *in); | 60 | int csv_read(csv_t *csv, FILE *in); |
| 61 | 61 | ||
| 62 | /* field access */ | 62 | /* field access */ |
| 63 | int csv_nfields(const csv_t *const csv); | 63 | size_t csv_nfields(const csv_t *const csv); |
| 64 | const char *csv_field(const csv_t *const csv, int idx); | 64 | const char *csv_field(const csv_t *const csv, size_t idx); |
| 65 | 65 | ||
| 66 | /* error handling */ | 66 | /* error handling */ |
| 67 | const char *csv_err_str(csv_err_t csv_err); | 67 | const char *csv_err_str(csv_err_t csv_err); |
