spvbin-helpers: Properly handle parsing strings with no destination.
[pspp] / src / output / spv / spvbin-helpers.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2018 Free Software Foundation, Inc.
3
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.
8
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.
13
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/>. */
16
17 #include <config.h>
18
19 #include "output/spv/spvbin-helpers.h"
20
21 #include <inttypes.h>
22 #include <string.h>
23
24 #include "libpspp/float-format.h"
25 #include "libpspp/integer-format.h"
26 #include "libpspp/str.h"
27
28 #include "gl/xmemdup0.h"
29
30 void
31 spvbin_input_init (struct spvbin_input *input, const void *data, size_t size)
32 {
33   *input = (struct spvbin_input) { .data = data, .size = size };
34 }
35
36 bool
37 spvbin_input_at_end (const struct spvbin_input *input)
38 {
39   return input->ofs >= input->size;
40 }
41
42 char *
43 spvbin_input_to_error (const struct spvbin_input *input, const char *name)
44 {
45   struct string s = DS_EMPTY_INITIALIZER;
46   if (name)
47     ds_put_format (&s, "%s: ", name);
48   ds_put_cstr (&s, "parse error decoding ");
49   for (size_t i = input->n_errors; i-- > 0;)
50     if (i < SPVBIN_MAX_ERRORS)
51       ds_put_format (&s, "/%s@%#zx", input->errors[i].name,
52                      input->errors[i].start);
53   ds_put_format (&s, " near %#zx", input->error_ofs);
54   return ds_steal_cstr (&s);
55 }
56
57 \f
58 bool
59 spvbin_match_bytes (struct spvbin_input *input, const void *bytes, size_t n)
60 {
61   if (input->size - input->ofs < n
62       || memcmp (&input->data[input->ofs], bytes, n))
63     return false;
64
65   input->ofs += n;
66   return true;
67 }
68
69 bool
70 spvbin_match_byte (struct spvbin_input *input, uint8_t byte)
71 {
72   return spvbin_match_bytes (input, &byte, 1);
73 }
74
75 bool
76 spvbin_parse_bool (struct spvbin_input *input, bool *p)
77 {
78   if (input->ofs >= input->size || input->data[input->ofs] > 1)
79     return false;
80   if (p)
81     *p = input->data[input->ofs];
82   input->ofs++;
83   return true;
84 }
85
86 static const void *
87 spvbin_parse__ (struct spvbin_input *input, size_t n)
88 {
89   if (input->size - input->ofs < n)
90     return NULL;
91
92   const void *src = &input->data[input->ofs];
93   input->ofs += n;
94   return src;
95 }
96
97 bool
98 spvbin_parse_byte (struct spvbin_input *input, uint8_t *p)
99 {
100   const void *src = spvbin_parse__ (input, sizeof *p);
101   if (src && p)
102     *p = *(const uint8_t *) src;
103   return src != NULL;
104 }
105
106 bool
107 spvbin_parse_int16 (struct spvbin_input *input, uint16_t *p)
108 {
109   const void *src = spvbin_parse__ (input, sizeof *p);
110   if (src && p)
111     *p = le_to_native16 (get_uint16 (src));
112   return src != NULL;
113 }
114
115 bool
116 spvbin_parse_int32 (struct spvbin_input *input, uint32_t *p)
117 {
118   const void *src = spvbin_parse__ (input, sizeof *p);
119   if (src && p)
120     *p = le_to_native32 (get_uint32 (src));
121   return src != NULL;
122 }
123
124 bool
125 spvbin_parse_int64 (struct spvbin_input *input, uint64_t *p)
126 {
127   const void *src = spvbin_parse__ (input, sizeof *p);
128   if (src && p)
129     *p = le_to_native64 (get_uint64 (src));
130   return src != NULL;
131 }
132
133 bool
134 spvbin_parse_be16 (struct spvbin_input *input, uint16_t *p)
135 {
136   const void *src = spvbin_parse__ (input, sizeof *p);
137   if (src && p)
138     *p = be_to_native16 (get_uint16 (src));
139   return src != NULL;
140 }
141
142 bool
143 spvbin_parse_be32 (struct spvbin_input *input, uint32_t *p)
144 {
145   const void *src = spvbin_parse__ (input, sizeof *p);
146   if (src && p)
147     *p = be_to_native32 (get_uint32 (src));
148   return src != NULL;
149 }
150
151 bool
152 spvbin_parse_be64 (struct spvbin_input *input, uint64_t *p)
153 {
154   const void *src = spvbin_parse__ (input, sizeof *p);
155   if (src && p)
156     *p = be_to_native64 (get_uint64 (src));
157   return src != NULL;
158 }
159
160 bool
161 spvbin_parse_double (struct spvbin_input *input, double *p)
162 {
163   const void *src = spvbin_parse__ (input, 8);
164   if (src && p)
165     *p = float_get_double (FLOAT_IEEE_DOUBLE_LE, src);
166   return src != NULL;
167 }
168
169 bool
170 spvbin_parse_float (struct spvbin_input *input, double *p)
171 {
172   const void *src = spvbin_parse__ (input, 4);
173   if (src && p)
174     *p = float_get_double (FLOAT_IEEE_SINGLE_LE, src);
175   return src != NULL;
176 }
177
178 static bool
179 spvbin_parse_string__ (struct spvbin_input *input,
180                        uint32_t (*raw_to_native32) (uint32_t),
181                        char **p)
182 {
183   if (p)
184     *p = NULL;
185
186   uint32_t length;
187   if (input->size - input->ofs < sizeof length)
188     return false;
189
190   const uint8_t *src = &input->data[input->ofs];
191   length = raw_to_native32 (get_uint32 (src));
192   if (input->size - input->ofs - sizeof length < length)
193     return false;
194
195   if (p)
196     *p = xmemdup0 (src + sizeof length, length);
197   input->ofs += sizeof length + length;
198   return true;
199 }
200
201 bool
202 spvbin_parse_string (struct spvbin_input *input, char **p)
203 {
204   return spvbin_parse_string__ (input, le_to_native32, p);
205 }
206
207 bool
208 spvbin_parse_bestring (struct spvbin_input *input, char **p)
209 {
210   return spvbin_parse_string__ (input, be_to_native32, p);
211 }
212
213 void
214 spvbin_error (struct spvbin_input *input, const char *name, size_t start)
215 {
216   if (!input->n_errors)
217     input->error_ofs = input->ofs;
218
219   /* We keep track of the error depth regardless of whether we can store all of
220      them.  The parser needs this to accurately save and restore error
221      state. */
222   if (input->n_errors < SPVBIN_MAX_ERRORS)
223     {
224       input->errors[input->n_errors].name = name;
225       input->errors[input->n_errors].start = start;
226     }
227   input->n_errors++;
228 }
229 \f
230 void
231 spvbin_print_header (const char *title, size_t start UNUSED, size_t len UNUSED, int indent)
232 {
233   for (int i = 0; i < indent * 4; i++)
234     putchar (' ');
235   fputs (title, stdout);
236 #if 0
237   if (start != SIZE_MAX)
238     printf (" (0x%zx, %zu)", start, len);
239 #endif
240   fputs (": ", stdout);
241 }
242
243 void
244 spvbin_print_presence (const char *title, int indent, bool present)
245 {
246   spvbin_print_header (title, -1, -1, indent);
247   puts (present ? "present" : "absent");
248 }
249
250 void
251 spvbin_print_bool (const char *title, int indent, bool x)
252 {
253   spvbin_print_header (title, -1, -1, indent);
254   printf ("%s\n", x ? "true" : "false");
255 }
256
257 void
258 spvbin_print_byte (const char *title, int indent, uint8_t x)
259 {
260   spvbin_print_header (title, -1, -1, indent);
261   printf ("%"PRIu8"\n", x);
262 }
263
264 void
265 spvbin_print_int16 (const char *title, int indent, uint16_t x)
266 {
267   spvbin_print_header (title, -1, -1, indent);
268   printf ("%"PRIu16"\n", x);
269 }
270
271 void
272 spvbin_print_int32 (const char *title, int indent, uint32_t x)
273 {
274   spvbin_print_header (title, -1, -1, indent);
275   printf ("%"PRIu32"\n", x);
276 }
277
278 void
279 spvbin_print_int64 (const char *title, int indent, uint64_t x)
280 {
281   spvbin_print_header (title, -1, -1, indent);
282   printf ("%"PRIu64"\n", x);
283 }
284
285 void
286 spvbin_print_double (const char *title, int indent, double x)
287 {
288   spvbin_print_header (title, -1, -1, indent);
289   printf ("%g\n", x);
290 }
291
292 void
293 spvbin_print_string (const char *title, int indent, const char *s)
294 {
295   spvbin_print_header (title, -1, -1, indent);
296   if (s)
297     printf ("\"%s\"\n", s);
298   else
299     printf ("none\n");
300 }
301
302 void
303 spvbin_print_case (const char *title, int indent, int x)
304 {
305   spvbin_print_header (title, -1, -1, indent);
306   printf ("%d\n", x);
307 }
308 \f
309 struct spvbin_position
310 spvbin_position_save (const struct spvbin_input *input)
311 {
312   struct spvbin_position pos = { input->ofs };
313   return pos;
314 }
315
316 void
317 spvbin_position_restore (struct spvbin_position *pos,
318                          struct spvbin_input *input)
319 {
320   input->ofs = pos->ofs;
321 }
322 \f
323 static bool
324 spvbin_limit_parse__ (struct spvbin_limit *limit, struct spvbin_input *input,
325                       uint32_t (*raw_to_native32) (uint32_t))
326 {
327   limit->size = input->size;
328
329   uint32_t count;
330   if (input->size - input->ofs < sizeof count)
331     return false;
332
333   const uint8_t *src = &input->data[input->ofs];
334   count = raw_to_native32 (get_uint32 (src));
335   if (input->size - input->ofs - sizeof count < count)
336     return false;
337
338   input->ofs += sizeof count;
339   input->size = input->ofs + count;
340   return true;
341 }
342
343 bool
344 spvbin_limit_parse (struct spvbin_limit *limit, struct spvbin_input *input)
345 {
346   return spvbin_limit_parse__ (limit, input, le_to_native32);
347 }
348
349 bool
350 spvbin_limit_parse_be (struct spvbin_limit *limit, struct spvbin_input *input)
351 {
352   return spvbin_limit_parse__ (limit, input, be_to_native32);
353 }
354
355 void
356 spvbin_limit_pop (struct spvbin_limit *limit, struct spvbin_input *input)
357 {
358   input->size = limit->size;
359 }