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