Coverage Report

Created: 2026-10-17 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/readstat/src/readstat_writer.c
Line
Count
Source
1
2
#include <stdlib.h>
3
#include <time.h>
4
#include "readstat.h"
5
#include "readstat_writer.h"
6
7
0
#define VARIABLES_INITIAL_CAPACITY    50
8
0
#define LABEL_SETS_INITIAL_CAPACITY   50
9
0
#define NOTES_INITIAL_CAPACITY        50
10
0
#define VALUE_LABELS_INITIAL_CAPACITY 10
11
0
#define STRING_REFS_INITIAL_CAPACITY 100
12
0
#define LABEL_SET_VARIABLES_INITIAL_CAPACITY 2
13
14
0
static readstat_error_t readstat_write_row_default_callback(void *writer_ctx, void *bytes, size_t len) {
15
0
    return readstat_write_bytes((readstat_writer_t *)writer_ctx, bytes, len);
16
0
}
17
18
0
static int readstat_compare_string_refs(const void *elem1, const void *elem2) {
19
0
    readstat_string_ref_t *ref1 = *(readstat_string_ref_t **)elem1;
20
0
    readstat_string_ref_t *ref2 = *(readstat_string_ref_t **)elem2;
21
22
0
    if (ref1->first_o == ref2->first_o)
23
0
        return ref1->first_v - ref2->first_v;
24
25
0
    return ref1->first_o - ref2->first_o;
26
0
}
27
28
0
readstat_string_ref_t *readstat_string_ref_init(const char *string) {
29
0
    size_t len = strlen(string) + 1;
30
0
    readstat_string_ref_t *ref = calloc(1, sizeof(readstat_string_ref_t) + len);
31
0
    if (ref == NULL) return NULL;
32
0
    ref->first_o = -1;
33
0
    ref->first_v = -1;
34
0
    ref->len = len;
35
0
    memcpy(&ref->data[0], string, len);
36
0
    return ref;
37
0
}
38
39
0
readstat_writer_t *readstat_writer_init(void) {
40
0
    readstat_writer_t *writer = calloc(1, sizeof(readstat_writer_t));
41
42
0
    writer->variables = calloc(VARIABLES_INITIAL_CAPACITY, sizeof(readstat_variable_t *));
43
0
    writer->variables_capacity = VARIABLES_INITIAL_CAPACITY;
44
45
0
    writer->label_sets = calloc(LABEL_SETS_INITIAL_CAPACITY, sizeof(readstat_label_set_t *));
46
0
    writer->label_sets_capacity = LABEL_SETS_INITIAL_CAPACITY;
47
48
0
    writer->notes = calloc(NOTES_INITIAL_CAPACITY, sizeof(char *));
49
0
    writer->notes_capacity = NOTES_INITIAL_CAPACITY;
50
51
0
    writer->string_refs = calloc(STRING_REFS_INITIAL_CAPACITY, sizeof(readstat_string_ref_t *));
52
0
    writer->string_refs_capacity = STRING_REFS_INITIAL_CAPACITY;
53
54
#ifdef TIME_UTC
55
    struct timespec now;
56
    if (timespec_get(&now, TIME_UTC) == TIME_UTC) {
57
        writer->timestamp = now.tv_sec;
58
        writer->timestamp_fraction = now.tv_nsec / 1e9;
59
    } else {
60
        writer->timestamp = time(NULL);
61
    }
62
#else
63
0
    writer->timestamp = time(NULL);
64
0
#endif
65
0
    writer->is_64bit = 1;
66
0
    writer->callbacks.write_row = &readstat_write_row_default_callback;
67
68
0
    return writer;
69
0
}
70
71
0
static void readstat_variable_free(readstat_variable_t *variable) {
72
0
    free(variable);
73
0
}
74
75
0
static void readstat_label_set_free(readstat_label_set_t *label_set) {
76
0
    int i;
77
0
    for (i=0; i<label_set->value_labels_count; i++) {
78
0
        readstat_value_label_t *value_label = readstat_get_value_label(label_set, i);
79
0
        if (value_label->label)
80
0
            free(value_label->label);
81
0
        if (value_label->string_key)
82
0
            free(value_label->string_key);
83
0
    }
84
0
    free(label_set->value_labels);
85
0
    free(label_set->variables);
86
0
    free(label_set);
87
0
}
88
89
0
static readstat_error_t readstat_copy_label(readstat_value_label_t *value_label, const char *label) {
90
0
    if (label && strlen(label)) {
91
0
        size_t label_len = strlen(label);
92
0
        if ((value_label->label = malloc(label_len + 1)) == NULL)
93
0
            return READSTAT_ERROR_MALLOC;
94
0
        memcpy(value_label->label, label, label_len + 1);
95
0
        value_label->label_len = label_len;
96
0
    }
97
0
    return READSTAT_OK;
98
0
}
99
100
0
static readstat_value_label_t *readstat_add_value_label(readstat_label_set_t *label_set, const char *label) {
101
0
    if (label_set->value_labels_count == label_set->value_labels_capacity) {
102
0
        void *tmp = realloc(label_set->value_labels,
103
0
                2 * label_set->value_labels_capacity * sizeof(readstat_value_label_t));
104
0
        if (tmp == NULL) return NULL;
105
0
        label_set->value_labels = tmp;
106
0
        label_set->value_labels_capacity *= 2;
107
0
    }
108
0
    readstat_value_label_t *new_value_label = &label_set->value_labels[label_set->value_labels_count++];
109
0
    memset(new_value_label, 0, sizeof(readstat_value_label_t));
110
0
    if (readstat_copy_label(new_value_label, label) != READSTAT_OK)
111
0
        return NULL;
112
0
    return new_value_label;
113
0
}
114
115
0
readstat_error_t readstat_validate_variable(readstat_writer_t *writer, const readstat_variable_t *variable) {
116
0
    if (!writer->initialized)
117
0
        return READSTAT_ERROR_WRITER_NOT_INITIALIZED;
118
119
0
    if (writer->callbacks.variable_ok)
120
0
        return writer->callbacks.variable_ok(variable);
121
122
0
    return READSTAT_OK;
123
0
}
124
125
0
readstat_error_t readstat_validate_metadata(readstat_writer_t *writer) {
126
0
    if (!writer->initialized)
127
0
        return READSTAT_ERROR_WRITER_NOT_INITIALIZED;
128
129
0
    if (writer->callbacks.metadata_ok)
130
0
        return writer->callbacks.metadata_ok(writer);
131
132
0
    return READSTAT_OK;
133
0
}
134
135
0
static readstat_error_t readstat_begin_writing_data(readstat_writer_t *writer) {
136
0
    readstat_error_t retval = READSTAT_OK;
137
138
0
    size_t row_len = 0;
139
0
    int i;
140
141
0
    retval = readstat_validate_metadata(writer);
142
0
    if (retval != READSTAT_OK)
143
0
        goto cleanup;
144
145
0
    for (i=0; i<writer->variables_count; i++) {
146
0
        readstat_variable_t *variable = readstat_get_variable(writer, i);
147
0
        variable->storage_width = writer->callbacks.variable_width(variable->type, variable->user_width);
148
0
        variable->offset = row_len;
149
0
        row_len += variable->storage_width;
150
0
    }
151
0
    if (writer->callbacks.variable_ok) {
152
0
        for (i=0; i<writer->variables_count; i++) {
153
0
            retval = readstat_validate_variable(writer, readstat_get_variable(writer, i));
154
0
            if (retval != READSTAT_OK)
155
0
                goto cleanup;
156
0
        }
157
0
    }
158
0
    writer->row_len = row_len;
159
0
    writer->row = malloc(writer->row_len);
160
0
    if (writer->row == NULL) { retval = READSTAT_ERROR_MALLOC; goto cleanup; }
161
0
    if (writer->callbacks.begin_data) {
162
0
        retval = writer->callbacks.begin_data(writer);
163
0
    }
164
165
0
cleanup:
166
0
    return retval;
167
0
}
168
169
0
void readstat_writer_free(readstat_writer_t *writer) {
170
0
    int i;
171
0
    if (writer) {
172
0
        if (writer->callbacks.module_ctx_free && writer->module_ctx) {
173
0
            writer->callbacks.module_ctx_free(writer->module_ctx);
174
0
        }
175
0
        if (writer->variables) {
176
0
            for (i=0; i<writer->variables_count; i++) {
177
0
                readstat_variable_free(writer->variables[i]);
178
0
            }
179
0
            free(writer->variables);
180
0
        }
181
0
        if (writer->label_sets) {
182
0
            for (i=0; i<writer->label_sets_count; i++) {
183
0
                readstat_label_set_free(writer->label_sets[i]);
184
0
            }
185
0
            free(writer->label_sets);
186
0
        }
187
0
        if (writer->notes) {
188
0
            for (i=0; i<writer->notes_count; i++) {
189
0
                free(writer->notes[i]);
190
0
            }
191
0
            free(writer->notes);
192
0
        }
193
0
        if (writer->string_refs) {
194
0
            for (i=0; i<writer->string_refs_count; i++) {
195
0
                free(writer->string_refs[i]);
196
0
            }
197
0
            free(writer->string_refs);
198
0
        }
199
0
        if (writer->row) {
200
0
            free(writer->row);
201
0
        }
202
0
        free(writer);
203
0
    }
204
0
}
205
206
0
readstat_error_t readstat_set_data_writer(readstat_writer_t *writer, readstat_data_writer data_writer) {
207
0
    writer->data_writer = data_writer;
208
0
    return READSTAT_OK;
209
0
}
210
211
0
readstat_error_t readstat_write_bytes(readstat_writer_t *writer, const void *bytes, size_t len) {
212
0
    size_t bytes_written = writer->data_writer(bytes, len, writer->user_ctx);
213
0
    if (bytes_written < len) {
214
0
        return READSTAT_ERROR_WRITE;
215
0
    }
216
0
    writer->bytes_written += bytes_written;
217
0
    return READSTAT_OK;
218
0
}
219
220
readstat_error_t readstat_write_bytes_as_lines(readstat_writer_t *writer,
221
0
        const void *bytes, size_t len, size_t line_len, const char *line_sep) {
222
0
    size_t line_sep_len = strlen(line_sep);
223
0
    readstat_error_t retval = READSTAT_OK;
224
0
    size_t bytes_written = 0;
225
0
    while (bytes_written < len) {
226
0
        ssize_t bytes_left_in_line = line_len - (writer->bytes_written % (line_len + line_sep_len));
227
0
        if (len - bytes_written < bytes_left_in_line) {
228
0
            retval = readstat_write_bytes(writer, ((const char *)bytes) + bytes_written,
229
0
                    len - bytes_written);
230
0
            bytes_written = len;
231
0
        } else {
232
0
            retval = readstat_write_bytes(writer, ((const char *)bytes) + bytes_written,
233
0
                    bytes_left_in_line);
234
0
            bytes_written += bytes_left_in_line;
235
0
        }
236
0
        if (retval != READSTAT_OK)
237
0
            break;
238
239
0
        if (writer->bytes_written % (line_len + line_sep_len) == line_len) {
240
0
            if ((retval = readstat_write_bytes(writer, line_sep, line_sep_len)) != READSTAT_OK)
241
0
                break;
242
0
        }
243
0
    }
244
0
    return retval;
245
0
}
246
247
readstat_error_t readstat_write_line_padding(readstat_writer_t *writer, char pad,
248
0
        size_t line_len, const char *line_sep) {
249
0
    size_t line_sep_len = strlen(line_sep);
250
0
    if (writer->bytes_written % (line_len + line_sep_len) == 0)
251
0
        return READSTAT_OK;
252
253
0
    readstat_error_t error = READSTAT_OK;
254
0
    ssize_t bytes_left_in_line = line_len - (writer->bytes_written % (line_len + line_sep_len));
255
0
    char *bytes = malloc(bytes_left_in_line);
256
0
    if (bytes == NULL)
257
0
        return READSTAT_ERROR_MALLOC;
258
0
    memset(bytes, pad, bytes_left_in_line);
259
260
0
    if ((error = readstat_write_bytes(writer, bytes, bytes_left_in_line)) != READSTAT_OK)
261
0
        goto cleanup;
262
263
0
    if ((error = readstat_write_bytes(writer, line_sep, line_sep_len)) != READSTAT_OK)
264
0
        goto cleanup;
265
266
0
cleanup:
267
0
    if (bytes)
268
0
        free(bytes);
269
270
0
    return error;
271
0
}
272
273
0
readstat_error_t readstat_write_string(readstat_writer_t *writer, const char *bytes) {
274
0
    return readstat_write_bytes(writer, bytes, strlen(bytes));
275
0
}
276
277
0
static readstat_error_t readstat_write_repeated_byte(readstat_writer_t *writer, char byte, size_t len) {
278
0
    if (len == 0)
279
0
        return READSTAT_OK;
280
281
0
    char *zeros = malloc(len);
282
283
0
    memset(zeros, byte, len);
284
0
    readstat_error_t error = readstat_write_bytes(writer, zeros, len);
285
286
0
    free(zeros);
287
0
    return error;
288
0
}
289
290
0
readstat_error_t readstat_write_zeros(readstat_writer_t *writer, size_t len) {
291
0
    return readstat_write_repeated_byte(writer, '\0', len);
292
0
}
293
294
0
readstat_error_t readstat_write_spaces(readstat_writer_t *writer, size_t len) {
295
0
    return readstat_write_repeated_byte(writer, ' ', len);
296
0
}
297
298
0
readstat_error_t readstat_write_space_padded_string(readstat_writer_t *writer, const char *string, size_t max_len) {
299
0
    readstat_error_t retval = READSTAT_OK;
300
0
    if (string == NULL || string[0] == '\0')
301
0
        return readstat_write_spaces(writer, max_len);
302
303
0
    size_t len = strlen(string);
304
0
    if (len > max_len)
305
0
        len = max_len;
306
307
0
    if ((retval = readstat_write_bytes(writer, string, len)) != READSTAT_OK)
308
0
        return retval;
309
310
0
    return readstat_write_spaces(writer, max_len - len);
311
0
}
312
313
/* Once the first row has been begun (or readstat_end_writing called on an
314
 * empty data set) the file header, row layout, and section offsets have been
315
 * computed from the registered metadata, so nothing more may be added */
316
0
static int readstat_writer_data_has_begun(readstat_writer_t *writer) {
317
0
    return writer->row != NULL;
318
0
}
319
320
0
readstat_label_set_t *readstat_add_label_set(readstat_writer_t *writer, readstat_type_t type, const char *name) {
321
0
    if (readstat_writer_data_has_begun(writer))
322
0
        return NULL;
323
324
0
    if (writer->label_sets_count == writer->label_sets_capacity) {
325
0
        void *tmp = realloc(writer->label_sets,
326
0
                2 * writer->label_sets_capacity * sizeof(readstat_label_set_t *));
327
0
        if (tmp == NULL) return NULL;
328
0
        writer->label_sets = tmp;
329
0
        writer->label_sets_capacity *= 2;
330
0
    }
331
0
    readstat_label_set_t *new_label_set = calloc(1, sizeof(readstat_label_set_t));
332
    
333
0
    writer->label_sets[writer->label_sets_count++] = new_label_set;
334
335
0
    new_label_set->type = type;
336
0
    snprintf(new_label_set->name, sizeof(new_label_set->name), "%s", name);
337
338
0
    new_label_set->value_labels = calloc(VALUE_LABELS_INITIAL_CAPACITY, sizeof(readstat_value_label_t));
339
0
    new_label_set->value_labels_capacity = VALUE_LABELS_INITIAL_CAPACITY;
340
341
0
    new_label_set->variables = calloc(LABEL_SET_VARIABLES_INITIAL_CAPACITY, sizeof(readstat_variable_t *));
342
0
    new_label_set->variables_capacity = LABEL_SET_VARIABLES_INITIAL_CAPACITY;
343
344
0
    return new_label_set;
345
0
}
346
347
0
readstat_label_set_t *readstat_get_label_set(readstat_writer_t *writer, int index) {
348
0
    if (index < writer->label_sets_count) {
349
0
        return writer->label_sets[index];
350
0
    }
351
0
    return NULL;
352
0
}
353
354
void readstat_sort_label_set(readstat_label_set_t *label_set,
355
0
        int (*compare)(const readstat_value_label_t *, const readstat_value_label_t *)) {
356
0
    qsort(label_set->value_labels, label_set->value_labels_count, sizeof(readstat_value_label_t),
357
0
            (int (*)(const void *, const void *))compare);
358
0
}
359
360
0
readstat_value_label_t *readstat_get_value_label(readstat_label_set_t *label_set, int index) {
361
0
    if (index < label_set->value_labels_count) {
362
0
        return &label_set->value_labels[index];
363
0
    }
364
0
    return NULL;
365
0
}
366
367
0
readstat_variable_t *readstat_get_label_set_variable(readstat_label_set_t *label_set, int index) {
368
0
    if (index < label_set->variables_count) {
369
0
        return ((readstat_variable_t **)label_set->variables)[index];
370
0
    }
371
0
    return NULL;
372
0
}
373
374
0
void readstat_label_double_value(readstat_label_set_t *label_set, double value, const char *label) {
375
0
    readstat_value_label_t *new_value_label = readstat_add_value_label(label_set, label);
376
0
    if (new_value_label == NULL)
377
0
        return;
378
0
    new_value_label->double_key = value;
379
0
    new_value_label->int32_key = value;
380
0
}
381
382
0
void readstat_label_int32_value(readstat_label_set_t *label_set, int32_t value, const char *label) {
383
0
    readstat_value_label_t *new_value_label = readstat_add_value_label(label_set, label);
384
0
    if (new_value_label == NULL)
385
0
        return;
386
0
    new_value_label->double_key = value;
387
0
    new_value_label->int32_key = value;
388
0
}
389
390
0
void readstat_label_string_value(readstat_label_set_t *label_set, const char *value, const char *label) {
391
0
    readstat_value_label_t *new_value_label = readstat_add_value_label(label_set, label);
392
0
    if (new_value_label == NULL)
393
0
        return;
394
0
    if (value && strlen(value)) {
395
0
        size_t string_key_len = strlen(value);
396
0
        if ((new_value_label->string_key = malloc(string_key_len + 1)) == NULL)
397
0
            return;
398
0
        memcpy(new_value_label->string_key, value, string_key_len + 1);
399
0
        new_value_label->string_key_len = string_key_len;
400
0
    }
401
0
}
402
403
0
void readstat_label_tagged_value(readstat_label_set_t *label_set, char tag, const char *label) {
404
0
    readstat_value_label_t *new_value_label = readstat_add_value_label(label_set, label);
405
0
    if (new_value_label == NULL)
406
0
        return;
407
0
    new_value_label->tag = tag;
408
0
}
409
410
0
readstat_variable_t *readstat_add_variable(readstat_writer_t *writer, const char *name, readstat_type_t type, size_t width) {
411
0
    if (readstat_writer_data_has_begun(writer))
412
0
        return NULL;
413
414
0
    if (writer->variables_count == writer->variables_capacity) {
415
0
        void *tmp = realloc(writer->variables,
416
0
                2 * writer->variables_capacity * sizeof(readstat_variable_t *));
417
0
        if (tmp == NULL) return NULL;
418
0
        writer->variables = tmp;
419
0
        writer->variables_capacity *= 2;
420
0
    }
421
0
    readstat_variable_t *new_variable = calloc(1, sizeof(readstat_variable_t));
422
423
0
    new_variable->index = writer->variables_count++;
424
    
425
0
    writer->variables[new_variable->index] = new_variable;
426
427
0
    new_variable->user_width = width;
428
0
    new_variable->type = type;
429
430
0
    if (readstat_variable_get_type_class(new_variable) == READSTAT_TYPE_CLASS_STRING) {
431
0
        new_variable->alignment = READSTAT_ALIGNMENT_LEFT;
432
0
    } else {
433
0
        new_variable->alignment = READSTAT_ALIGNMENT_RIGHT;
434
0
    }
435
0
    new_variable->measure = READSTAT_MEASURE_UNKNOWN;
436
437
0
    if (name) {
438
0
        snprintf(new_variable->name, sizeof(new_variable->name), "%s", name);
439
0
    }
440
441
0
    return new_variable;
442
0
}
443
444
0
static void readstat_append_string_ref(readstat_writer_t *writer, readstat_string_ref_t *ref) {
445
0
    if (writer->string_refs_count == writer->string_refs_capacity) {
446
0
        void *tmp = realloc(writer->string_refs,
447
0
                2 * writer->string_refs_capacity * sizeof(readstat_string_ref_t *));
448
0
        if (tmp == NULL) return;
449
0
        writer->string_refs = tmp;
450
0
        writer->string_refs_capacity *= 2;
451
0
    }
452
0
    writer->string_refs[writer->string_refs_count++] = ref;
453
0
}
454
455
0
readstat_string_ref_t *readstat_add_string_ref(readstat_writer_t *writer, const char *string) {
456
0
    if (readstat_writer_data_has_begun(writer))
457
0
        return NULL;
458
459
0
    readstat_string_ref_t *ref = readstat_string_ref_init(string);
460
0
    if (ref == NULL)
461
0
        return NULL;
462
463
0
    readstat_append_string_ref(writer, ref);
464
0
    return ref;
465
0
}
466
467
0
void readstat_add_note(readstat_writer_t *writer, const char *note) {
468
0
    if (readstat_writer_data_has_begun(writer))
469
0
        return;
470
471
0
    if (writer->notes_count == writer->notes_capacity) {
472
0
        void *tmp = realloc(writer->notes,
473
0
                2 * writer->notes_capacity * sizeof(const char *));
474
0
        if (tmp == NULL) return;
475
0
        writer->notes = tmp;
476
0
        writer->notes_capacity *= 2;
477
0
    }
478
0
    char *note_copy = malloc(strlen(note) + 1);
479
0
    strcpy(note_copy, note);
480
0
    writer->notes[writer->notes_count++] = note_copy;
481
0
}
482
483
0
void readstat_variable_set_label(readstat_variable_t *variable, const char *label) {
484
0
    if (label) {
485
0
        snprintf(variable->label, sizeof(variable->label), "%s", label);
486
0
    } else {
487
0
        memset(variable->label, '\0', sizeof(variable->label));
488
0
    }
489
0
}
490
491
0
void readstat_variable_set_format(readstat_variable_t *variable, const char *format) {
492
0
    if (format) {
493
0
        snprintf(variable->format, sizeof(variable->format), "%s", format);
494
0
    } else {
495
0
        memset(variable->format, '\0', sizeof(variable->format));
496
0
    }
497
0
}
498
499
0
void readstat_variable_set_informat(readstat_variable_t *variable, const char *informat) {
500
0
    if (informat) {
501
0
        snprintf(variable->informat, sizeof(variable->informat), "%s", informat);
502
0
    } else {
503
0
        memset(variable->informat, '\0', sizeof(variable->informat));
504
0
    }
505
0
}
506
507
0
void readstat_variable_set_measure(readstat_variable_t *variable, readstat_measure_t measure) {
508
0
    variable->measure = measure;
509
0
}
510
511
0
void readstat_variable_set_alignment(readstat_variable_t *variable, readstat_alignment_t alignment) {
512
0
    variable->alignment = alignment;
513
0
}
514
515
0
void readstat_variable_set_display_width(readstat_variable_t *variable, int display_width) {
516
0
    variable->display_width = display_width;
517
0
}
518
519
0
void readstat_variable_set_label_set(readstat_variable_t *variable, readstat_label_set_t *label_set) {
520
0
    variable->label_set = label_set;
521
0
    if (label_set) {
522
0
        if (label_set->variables_count == label_set->variables_capacity) {
523
0
            void *tmp = realloc(label_set->variables,
524
0
                    2 * label_set->variables_capacity * sizeof(readstat_variable_t *));
525
0
            if (tmp == NULL) return;
526
0
            label_set->variables = tmp;
527
0
            label_set->variables_capacity *= 2;
528
0
        }
529
0
        ((readstat_variable_t **)label_set->variables)[label_set->variables_count++] = variable;
530
0
    }
531
0
}
532
533
0
readstat_variable_t *readstat_get_variable(readstat_writer_t *writer, int index) {
534
0
    if (index < writer->variables_count) {
535
0
        return writer->variables[index];
536
0
    }
537
0
    return NULL;
538
0
}
539
540
0
readstat_string_ref_t *readstat_get_string_ref(readstat_writer_t *writer, int index) {
541
0
    if (index < writer->string_refs_count) {
542
0
        return writer->string_refs[index];
543
0
    }
544
0
    return NULL;
545
0
}
546
547
0
readstat_error_t readstat_writer_set_file_label(readstat_writer_t *writer, const char *file_label) {
548
0
    snprintf(writer->file_label, sizeof(writer->file_label), "%s", file_label);
549
0
    return READSTAT_OK;
550
0
}
551
552
0
readstat_error_t readstat_writer_set_file_timestamp(readstat_writer_t *writer, time_t timestamp) {
553
0
    writer->timestamp = timestamp;
554
0
    writer->timestamp_fraction = 0.0;
555
0
    return READSTAT_OK;
556
0
}
557
558
0
readstat_error_t readstat_writer_set_table_name(readstat_writer_t *writer, const char *table_name) {
559
0
    snprintf(writer->table_name, sizeof(writer->table_name), "%s", table_name);
560
0
    return READSTAT_OK;
561
0
}
562
563
0
readstat_error_t readstat_writer_set_fweight_variable(readstat_writer_t *writer, const readstat_variable_t *variable) {
564
0
    if (readstat_variable_get_type_class(variable) == READSTAT_TYPE_CLASS_STRING)
565
0
        return READSTAT_ERROR_BAD_FREQUENCY_WEIGHT;
566
567
0
    writer->fweight_variable = variable;
568
0
    return READSTAT_OK;
569
0
}
570
571
0
readstat_error_t readstat_writer_set_file_format_version(readstat_writer_t *writer, uint8_t version) {
572
0
    writer->version = version;
573
0
    return READSTAT_OK;
574
0
}
575
576
0
readstat_error_t readstat_writer_set_file_format_is_64bit(readstat_writer_t *writer, int is_64bit) {
577
0
    writer->is_64bit = is_64bit;
578
0
    return READSTAT_OK;
579
0
}
580
581
readstat_error_t readstat_writer_set_compression(readstat_writer_t *writer, 
582
0
        readstat_compress_t compression) {
583
0
    writer->compression = compression;
584
0
    return READSTAT_OK;
585
0
}
586
587
readstat_error_t readstat_writer_set_error_handler(readstat_writer_t *writer, 
588
0
        readstat_error_handler error_handler) {
589
0
    writer->error_handler = error_handler;
590
0
    return READSTAT_OK;
591
0
}
592
593
0
readstat_error_t readstat_begin_writing_file(readstat_writer_t *writer, void *user_ctx, long row_count) {
594
0
    writer->row_count = row_count;
595
0
    writer->user_ctx = user_ctx;
596
597
0
    writer->initialized = 1;
598
599
0
    return readstat_validate_metadata(writer);
600
0
}
601
602
0
readstat_error_t readstat_begin_row(readstat_writer_t *writer) {
603
0
    readstat_error_t retval = READSTAT_OK;
604
0
    if (!writer->initialized)
605
0
        return READSTAT_ERROR_WRITER_NOT_INITIALIZED;
606
607
0
    if (writer->current_row == 0)
608
0
        retval = readstat_begin_writing_data(writer);
609
610
0
    if (retval != READSTAT_OK || writer->row == NULL)
611
0
        return retval;
612
613
0
    memset(writer->row, '\0', writer->row_len);
614
0
    return retval;
615
0
}
616
617
// Then call one of these for each variable
618
0
readstat_error_t readstat_insert_int8_value(readstat_writer_t *writer, const readstat_variable_t *variable, int8_t value) {
619
0
    if (!writer->initialized || writer->row == NULL)
620
0
        return READSTAT_ERROR_WRITER_NOT_INITIALIZED;
621
0
    if (variable->type != READSTAT_TYPE_INT8)
622
0
        return READSTAT_ERROR_VALUE_TYPE_MISMATCH;
623
624
0
    return writer->callbacks.write_int8(&writer->row[variable->offset], variable, value);
625
0
}
626
627
0
readstat_error_t readstat_insert_int16_value(readstat_writer_t *writer, const readstat_variable_t *variable, int16_t value) {
628
0
    if (!writer->initialized || writer->row == NULL)
629
0
        return READSTAT_ERROR_WRITER_NOT_INITIALIZED;
630
0
    if (variable->type != READSTAT_TYPE_INT16)
631
0
        return READSTAT_ERROR_VALUE_TYPE_MISMATCH;
632
633
0
    return writer->callbacks.write_int16(&writer->row[variable->offset], variable, value);
634
0
}
635
636
0
readstat_error_t readstat_insert_int32_value(readstat_writer_t *writer, const readstat_variable_t *variable, int32_t value) {
637
0
    if (!writer->initialized || writer->row == NULL)
638
0
        return READSTAT_ERROR_WRITER_NOT_INITIALIZED;
639
0
    if (variable->type != READSTAT_TYPE_INT32)
640
0
        return READSTAT_ERROR_VALUE_TYPE_MISMATCH;
641
642
0
    return writer->callbacks.write_int32(&writer->row[variable->offset], variable, value);
643
0
}
644
645
0
readstat_error_t readstat_insert_float_value(readstat_writer_t *writer, const readstat_variable_t *variable, float value) {
646
0
    if (!writer->initialized || writer->row == NULL)
647
0
        return READSTAT_ERROR_WRITER_NOT_INITIALIZED;
648
0
    if (variable->type != READSTAT_TYPE_FLOAT)
649
0
        return READSTAT_ERROR_VALUE_TYPE_MISMATCH;
650
651
0
    return writer->callbacks.write_float(&writer->row[variable->offset], variable, value);
652
0
}
653
654
0
readstat_error_t readstat_insert_double_value(readstat_writer_t *writer, const readstat_variable_t *variable, double value) {
655
0
    if (!writer->initialized || writer->row == NULL)
656
0
        return READSTAT_ERROR_WRITER_NOT_INITIALIZED;
657
0
    if (variable->type != READSTAT_TYPE_DOUBLE)
658
0
        return READSTAT_ERROR_VALUE_TYPE_MISMATCH;
659
660
0
    return writer->callbacks.write_double(&writer->row[variable->offset], variable, value);
661
0
}
662
663
0
readstat_error_t readstat_insert_string_value(readstat_writer_t *writer, const readstat_variable_t *variable, const char *value) {
664
0
    if (!writer->initialized || writer->row == NULL)
665
0
        return READSTAT_ERROR_WRITER_NOT_INITIALIZED;
666
0
    if (variable->type != READSTAT_TYPE_STRING)
667
0
        return READSTAT_ERROR_VALUE_TYPE_MISMATCH;
668
669
0
    return writer->callbacks.write_string(&writer->row[variable->offset], variable, value);
670
0
}
671
672
0
readstat_error_t readstat_insert_string_ref(readstat_writer_t *writer, const readstat_variable_t *variable, readstat_string_ref_t *ref) {
673
0
    if (!writer->initialized || writer->row == NULL)
674
0
        return READSTAT_ERROR_WRITER_NOT_INITIALIZED;
675
0
    if (variable->type != READSTAT_TYPE_STRING_REF)
676
0
        return READSTAT_ERROR_VALUE_TYPE_MISMATCH;
677
0
    if (!writer->callbacks.write_string_ref)
678
0
        return READSTAT_ERROR_STRING_REFS_NOT_SUPPORTED;
679
680
0
    if (ref && ref->first_o == -1 && ref->first_v == -1) {
681
0
        ref->first_o = writer->current_row + 1;
682
0
        ref->first_v = variable->index + 1;
683
0
    }
684
685
0
    return writer->callbacks.write_string_ref(&writer->row[variable->offset], variable, ref);
686
0
}
687
688
0
readstat_error_t readstat_insert_missing_value(readstat_writer_t *writer, const readstat_variable_t *variable) {
689
0
    if (!writer->initialized || writer->row == NULL)
690
0
        return READSTAT_ERROR_WRITER_NOT_INITIALIZED;
691
692
0
    if (variable->type == READSTAT_TYPE_STRING) {
693
0
        return writer->callbacks.write_missing_string(&writer->row[variable->offset], variable);
694
0
    }
695
0
    if (variable->type == READSTAT_TYPE_STRING_REF) {
696
0
        return readstat_insert_string_ref(writer, variable, NULL);
697
0
    }
698
699
0
    return writer->callbacks.write_missing_number(&writer->row[variable->offset], variable);
700
0
}
701
702
0
readstat_error_t readstat_insert_tagged_missing_value(readstat_writer_t *writer, const readstat_variable_t *variable, char tag) {
703
0
    if (!writer->initialized || writer->row == NULL)
704
0
        return READSTAT_ERROR_WRITER_NOT_INITIALIZED;
705
0
    if (!writer->callbacks.write_missing_tagged) {
706
        /* Write out a missing number but return an error */
707
0
        writer->callbacks.write_missing_number(&writer->row[variable->offset], variable);
708
0
        return READSTAT_ERROR_TAGGED_VALUES_NOT_SUPPORTED;
709
0
    }
710
711
0
    return writer->callbacks.write_missing_tagged(&writer->row[variable->offset], variable, tag);
712
0
}
713
714
0
readstat_error_t readstat_end_row(readstat_writer_t *writer) {
715
0
    if (!writer->initialized)
716
0
        return READSTAT_ERROR_WRITER_NOT_INITIALIZED;
717
718
0
    readstat_error_t error = writer->callbacks.write_row(writer, writer->row, writer->row_len);
719
0
    if (error == READSTAT_OK)
720
0
        writer->current_row++;
721
0
    return error;
722
0
}
723
724
0
readstat_error_t readstat_end_writing(readstat_writer_t *writer) {
725
0
    if (!writer->initialized)
726
0
        return READSTAT_ERROR_WRITER_NOT_INITIALIZED;
727
728
0
    if (writer->current_row != writer->row_count)
729
0
        return READSTAT_ERROR_ROW_COUNT_MISMATCH;
730
731
0
    if (writer->row_count == 0) {
732
0
        readstat_error_t retval = readstat_begin_writing_data(writer);
733
0
        if (retval != READSTAT_OK)
734
0
            return retval;
735
0
    }
736
737
    /* Sort if out of order */
738
0
    int i;
739
0
    for (i=1; i<writer->string_refs_count; i++) {
740
0
        if (readstat_compare_string_refs(&writer->string_refs[i-1], &writer->string_refs[i]) > 0) {
741
0
            qsort(writer->string_refs, writer->string_refs_count, 
742
0
                    sizeof(readstat_string_ref_t *), &readstat_compare_string_refs);
743
0
            break;
744
0
        }
745
0
    }
746
747
0
    if (!writer->callbacks.end_data)
748
0
        return READSTAT_OK;
749
750
0
    return writer->callbacks.end_data(writer);
751
0
}