1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2018 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19 #include "output/spv/spv-legacy-data.h"
26 #include "libpspp/cast.h"
27 #include "libpspp/float-format.h"
28 #include "data/val-type.h"
29 #include "output/spv/old-binary-parser.h"
31 #include "gl/minmax.h"
32 #include "gl/xalloc.h"
33 #include "gl/xmemdup0.h"
35 #include "gl/xvasprintf.h"
38 spv_data_uninit (struct spv_data *data)
43 for (size_t i = 0; i < data->n_sources; i++)
44 spv_data_source_uninit (&data->sources[i]);
49 spv_data_dump (const struct spv_data *data, FILE *stream)
51 for (size_t i = 0; i < data->n_sources; i++)
55 spv_data_source_dump (&data->sources[i], stream);
59 struct spv_data_source *
60 spv_data_find_source (const struct spv_data *data, const char *source_name)
62 for (size_t i = 0; i < data->n_sources; i++)
63 if (!strcmp (data->sources[i].source_name, source_name))
64 return &data->sources[i];
69 struct spv_data_variable *
70 spv_data_find_variable (const struct spv_data *data,
71 const char *source_name,
72 const char *variable_name)
74 struct spv_data_source *source = spv_data_find_source (data, source_name);
75 return source ? spv_data_source_find_variable (source, variable_name) : NULL;
79 spv_data_source_uninit (struct spv_data_source *source)
84 for (size_t i = 0; i < source->n_vars; i++)
85 spv_data_variable_uninit (&source->vars[i]);
87 free (source->source_name);
91 spv_data_source_dump (const struct spv_data_source *source, FILE *stream)
93 fprintf (stream, "source \"%s\" (%zu values):\n",
94 source->source_name, source->n_values);
95 for (size_t i = 0; i < source->n_vars; i++)
96 spv_data_variable_dump (&source->vars[i], stream);
99 struct spv_data_variable *
100 spv_data_source_find_variable (const struct spv_data_source *source,
101 const char *variable_name)
103 for (size_t i = 0; i < source->n_vars; i++)
104 if (!strcmp (source->vars[i].var_name, variable_name))
105 return &source->vars[i];
110 spv_data_variable_uninit (struct spv_data_variable *var)
115 free (var->var_name);
116 for (size_t i = 0; i < var->n_values; i++)
117 spv_data_value_uninit (&var->values[i]);
122 spv_data_variable_dump (const struct spv_data_variable *var, FILE *stream)
124 fprintf (stream, "variable \"%s\":", var->var_name);
125 for (size_t i = 0; i < var->n_values; i++)
130 spv_data_value_dump (&var->values[i], stream);
136 spv_data_value_uninit (struct spv_data_value *value)
138 if (value && value->width >= 0)
143 spv_data_value_equal (const struct spv_data_value *a,
144 const struct spv_data_value *b)
146 return (a->width == b->width
147 && a->index == b->index
150 : !strcmp (a->s, b->s)));
153 struct spv_data_value *
154 spv_data_values_clone (const struct spv_data_value *src, size_t n)
156 struct spv_data_value *dst = xmemdup (src, n * sizeof *src);
157 for (size_t i = 0; i < n; i++)
158 if (dst[i].width >= 0)
159 dst[i].s = xstrdup (dst[i].s);
164 spv_data_value_dump (const struct spv_data_value *value, FILE *stream)
166 if (value->index != SYSMIS)
167 fprintf (stream, "%.*ge-", DBL_DIG + 1, value->index);
168 if (value->width >= 0)
169 fprintf (stream, "\"%s\"", value->s);
170 else if (value->d == SYSMIS)
173 fprintf (stream, "%.*g", DBL_DIG + 1, value->d);
177 decode_fixed_string (const uint8_t *buf_, size_t size)
179 const char *buf = CHAR_CAST (char *, buf_);
180 return xmemdup0 (buf, strnlen (buf, size));
184 decode_var_name (const struct spvob_metadata *md)
186 int n0 = strnlen ((char *) md->source_name, sizeof md->source_name);
187 int n1 = (n0 < sizeof md->source_name ? 0
188 : strnlen ((char *) md->ext_source_name,
189 sizeof md->ext_source_name));
190 return xasprintf ("%.*s%.*s",
191 n0, (char *) md->source_name,
192 n1, (char *) md->ext_source_name);
195 static char * WARN_UNUSED_RESULT
196 decode_data (const uint8_t *in, size_t size, size_t data_offset,
197 struct spv_data_source *source, size_t *end_offsetp)
199 size_t var_size = xsum (288, xtimes (source->n_values, 8));
200 size_t source_size = xtimes (source->n_vars, var_size);
201 size_t end_offset = xsum (data_offset, source_size);
202 if (size_overflow_p (end_offset))
203 return xasprintf ("Data source \"%s\" exceeds supported %zu-byte size.",
204 source->source_name, SIZE_MAX - 1);
205 if (end_offset > size)
206 return xasprintf ("%zu-byte data source \"%s\" starting at offset %#zx "
207 "runs past end of %zu-byte ZIP member.",
208 source_size, source->source_name, data_offset,
212 for (size_t i = 0; i < source->n_vars; i++)
214 struct spv_data_variable *var = &source->vars[i];
215 var->var_name = decode_fixed_string (in, 288);
218 var->values = xnmalloc (source->n_values, sizeof *var->values);
219 var->n_values = source->n_values;
220 for (size_t j = 0; j < source->n_values; j++)
222 var->values[j].index = SYSMIS;
223 var->values[j].width = -1;
224 var->values[j].d = float_get_double (FLOAT_IEEE_DOUBLE_LE, in);
229 *end_offsetp = end_offset;
233 static char * WARN_UNUSED_RESULT
234 decode_variable_map (const char *source_name,
235 const struct spvob_variable_map *in,
236 const struct spvob_labels *labels,
237 struct spv_data_variable *out)
239 if (strcmp (in->variable_name, out->var_name))
240 return xasprintf ("Source \"%s\" variable \"%s\" mapping is associated "
241 "with wrong variable \"%s\".",
242 source_name, out->var_name, in->variable_name);
244 for (size_t i = 0; i < in->n_data; i++)
246 const struct spvob_datum_map *map = in->data[i];
248 if (map->value_idx >= out->n_values)
249 return xasprintf ("Source \"%s\" variable \"%s\" mapping %zu "
250 "attempts to set 0-based value %"PRIu32" "
251 "but source has only %zu values.",
252 source_name, out->var_name, i,
253 map->value_idx, out->n_values);
254 struct spv_data_value *value = &out->values[map->value_idx];
256 if (map->label_idx >= labels->n_labels)
257 return xasprintf ("Source \"%s\" variable \"%s\" mapping %zu "
258 "attempts to set value %"PRIu32" to 0-based label "
259 "%"PRIu32" but only %"PRIu32" labels are present.",
260 source_name, out->var_name, i,
261 map->value_idx, map->label_idx, labels->n_labels);
262 const struct spvob_label *label = labels->labels[map->label_idx];
264 if (value->width >= 0)
265 return xasprintf ("Source \"%s\" variable \"%s\" mapping %zu "
266 "attempts to change string value %"PRIu32".",
267 source_name, out->var_name, i,
270 else if (value->d != SYSMIS && !isnan (value->d))
275 return xasprintf ("Source \"%s\" variable \"%s\" mapping %zu "
276 "attempts to change non-missing value %"PRIu32" "
278 source_name, out->var_name, i,
285 value->width = strlen (label->label);
286 value->s = xmemdup0 (label->label, value->width);
292 static char * WARN_UNUSED_RESULT
293 decode_source_map (const struct spvob_source_map *in,
294 const struct spvob_labels *labels,
295 struct spv_data_source *out)
297 if (in->n_variables > out->n_vars)
298 return xasprintf ("source map for \"%s\" has %"PRIu32" variables but "
299 "source has only %zu",
300 out->source_name, in->n_variables, out->n_vars);
302 for (size_t i = 0; i < in->n_variables; i++)
304 char *error = decode_variable_map (out->source_name, in->variables[i],
305 labels, &out->vars[i]);
313 static char * WARN_UNUSED_RESULT
314 decode_strings (const struct spvob_strings *in, struct spv_data *out)
316 for (size_t i = 0; i < in->maps->n_maps; i++)
318 const struct spvob_source_map *sm = in->maps->maps[i];
319 const char *name = sm->source_name;
320 struct spv_data_source *source = spv_data_find_source (out, name);
322 return xasprintf ("cannot decode source map for unknown source \"%s\"",
325 char *error = decode_source_map (sm, in->labels, source);
333 char * WARN_UNUSED_RESULT
334 spv_legacy_data_decode (const uint8_t *in, size_t size, struct spv_data *out)
337 memset (out, 0, sizeof *out);
339 struct spvbin_input input;
340 spvbin_input_init (&input, in, size);
342 struct spvob_legacy_binary *lb;
343 bool ok = spvob_parse_legacy_binary (&input, &lb);
346 error = spvbin_input_to_error (&input, NULL);
350 out->sources = XCALLOC (lb->n_sources, struct spv_data_source);
351 out->n_sources = lb->n_sources;
353 for (size_t i = 0; i < lb->n_sources; i++)
355 const struct spvob_metadata *md = lb->metadata[i];
356 struct spv_data_source *source = &out->sources[i];
358 source->source_name = decode_var_name (md);
359 source->n_vars = md->n_variables;
360 source->n_values = md->n_values;
361 source->vars = xcalloc (md->n_variables, sizeof *source->vars);
364 error = decode_data (in, size, md->data_offset, source, &end);
368 input.ofs = MAX (input.ofs, end);
371 if (input.ofs < input.size)
373 struct spvob_strings *strings;
374 bool ok = spvob_parse_strings (&input, &strings);
376 error = spvbin_input_to_error (&input, NULL);
379 if (input.ofs != input.size)
380 error = xasprintf ("expected end of file at offset #%zx",
383 error = decode_strings (strings, out);
384 spvob_free_strings (strings);
391 spvob_free_legacy_binary (lb);
396 spv_data_uninit (out);
397 memset (out, 0, sizeof *out);
398 spvob_free_legacy_binary (lb);