Add support for reading and writing SPV files.
[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   *p = NULL;
184
185   uint32_t length;
186   if (input->size - input->ofs < sizeof length)
187     return false;
188
189   const uint8_t *src = &input->data[input->ofs];
190   length = raw_to_native32 (get_uint32 (src));
191   if (input->size - input->ofs - sizeof length < length)
192     return false;
193
194   if (p)
195     *p = xmemdup0 (src + sizeof length, length);
196   input->ofs += sizeof length + length;
197   return true;
198 }
199
200 bool
201 spvbin_parse_string (struct spvbin_input *input, char **p)
202 {
203   return spvbin_parse_string__ (input, le_to_native32, p);
204 }
205
206 bool
207 spvbin_parse_bestring (struct spvbin_input *input, char **p)
208 {
209   return spvbin_parse_string__ (input, be_to_native32, p);
210 }
211
212 void
213 spvbin_error (struct spvbin_input *input, const char *name, size_t start)
214 {
215   if (!input->n_errors)
216     input->error_ofs = input->ofs;
217
218   /* We keep track of the error depth regardless of whether we can store all of
219      them.  The parser needs this to accurately save and restore error
220      state. */
221   if (input->n_errors < SPVBIN_MAX_ERRORS)
222     {
223       input->errors[input->n_errors].name = name;
224       input->errors[input->n_errors].start = start;
225     }
226   input->n_errors++;
227 }
228 \f
229 void
230 spvbin_print_header (const char *title, size_t start UNUSED, size_t len UNUSED, int indent)
231 {
232   for (int i = 0; i < indent * 4; i++)
233     putchar (' ');
234   fputs (title, stdout);
235 #if 0
236   if (start != SIZE_MAX)
237     printf (" (0x%zx, %zu)", start, len);
238 #endif
239   fputs (": ", stdout);
240 }
241
242 void
243 spvbin_print_presence (const char *title, int indent, bool present)
244 {
245   spvbin_print_header (title, -1, -1, indent);
246   puts (present ? "present" : "absent");
247 }
248
249 void
250 spvbin_print_bool (const char *title, int indent, bool x)
251 {
252   spvbin_print_header (title, -1, -1, indent);
253   printf ("%s\n", x ? "true" : "false");
254 }
255
256 void
257 spvbin_print_byte (const char *title, int indent, uint8_t x)
258 {
259   spvbin_print_header (title, -1, -1, indent);
260   printf ("%"PRIu8"\n", x);
261 }
262
263 void
264 spvbin_print_int16 (const char *title, int indent, uint16_t x)
265 {
266   spvbin_print_header (title, -1, -1, indent);
267   printf ("%"PRIu16"\n", x);
268 }
269
270 void
271 spvbin_print_int32 (const char *title, int indent, uint32_t x)
272 {
273   spvbin_print_header (title, -1, -1, indent);
274   printf ("%"PRIu32"\n", x);
275 }
276
277 void
278 spvbin_print_int64 (const char *title, int indent, uint64_t x)
279 {
280   spvbin_print_header (title, -1, -1, indent);
281   printf ("%"PRIu64"\n", x);
282 }
283
284 void
285 spvbin_print_double (const char *title, int indent, double x)
286 {
287   spvbin_print_header (title, -1, -1, indent);
288   printf ("%g\n", x);
289 }
290
291 void
292 spvbin_print_string (const char *title, int indent, const char *s)
293 {
294   spvbin_print_header (title, -1, -1, indent);
295   if (s)
296     printf ("\"%s\"\n", s);
297   else
298     printf ("none\n");
299 }
300
301 void
302 spvbin_print_case (const char *title, int indent, int x)
303 {
304   spvbin_print_header (title, -1, -1, indent);
305   printf ("%d\n", x);
306 }
307 \f
308 struct spvbin_position
309 spvbin_position_save (const struct spvbin_input *input)
310 {
311   struct spvbin_position pos = { input->ofs };
312   return pos;
313 }
314
315 void
316 spvbin_position_restore (struct spvbin_position *pos,
317                          struct spvbin_input *input)
318 {
319   input->ofs = pos->ofs;
320 }
321 \f
322 static bool
323 spvbin_limit_parse__ (struct spvbin_limit *limit, struct spvbin_input *input,
324                       uint32_t (*raw_to_native32) (uint32_t))
325 {
326   limit->size = input->size;
327
328   uint32_t count;
329   if (input->size - input->ofs < sizeof count)
330     return false;
331
332   const uint8_t *src = &input->data[input->ofs];
333   count = raw_to_native32 (get_uint32 (src));
334   if (input->size - input->ofs - sizeof count < count)
335     return false;
336
337   input->ofs += sizeof count;
338   input->size = input->ofs + count;
339   return true;
340 }
341
342 bool
343 spvbin_limit_parse (struct spvbin_limit *limit, struct spvbin_input *input)
344 {
345   return spvbin_limit_parse__ (limit, input, le_to_native32);
346 }
347
348 bool
349 spvbin_limit_parse_be (struct spvbin_limit *limit, struct spvbin_input *input)
350 {
351   return spvbin_limit_parse__ (limit, input, be_to_native32);
352 }
353
354 void
355 spvbin_limit_pop (struct spvbin_limit *limit, struct spvbin_input *input)
356 {
357   input->size = limit->size;
358 }