1 |
|
|
/* $OpenBSD: tables.c,v 1.4 2017/08/17 19:27:09 tedu Exp $ */ |
2 |
|
|
|
3 |
|
|
/* tables.c - tables serialization code |
4 |
|
|
* |
5 |
|
|
* Copyright (c) 1990 The Regents of the University of California. |
6 |
|
|
* All rights reserved. |
7 |
|
|
* |
8 |
|
|
* This code is derived from software contributed to Berkeley by |
9 |
|
|
* Vern Paxson. |
10 |
|
|
* |
11 |
|
|
* The United States Government has rights in this work pursuant |
12 |
|
|
* to contract no. DE-AC03-76SF00098 between the United States |
13 |
|
|
* Department of Energy and the University of California. |
14 |
|
|
* |
15 |
|
|
* This file is part of flex. |
16 |
|
|
* |
17 |
|
|
* Redistribution and use in source and binary forms, with or without |
18 |
|
|
* modification, are permitted provided that the following conditions |
19 |
|
|
* are met: |
20 |
|
|
* |
21 |
|
|
* 1. Redistributions of source code must retain the above copyright |
22 |
|
|
* notice, this list of conditions and the following disclaimer. |
23 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
24 |
|
|
* notice, this list of conditions and the following disclaimer in the |
25 |
|
|
* documentation and/or other materials provided with the distribution. |
26 |
|
|
* |
27 |
|
|
* Neither the name of the University nor the names of its contributors |
28 |
|
|
* may be used to endorse or promote products derived from this software |
29 |
|
|
* without specific prior written permission. |
30 |
|
|
* |
31 |
|
|
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR |
32 |
|
|
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED |
33 |
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
34 |
|
|
* PURPOSE. |
35 |
|
|
*/ |
36 |
|
|
|
37 |
|
|
|
38 |
|
|
#include "flexdef.h" |
39 |
|
|
#include "tables.h" |
40 |
|
|
|
41 |
|
|
/** Convert size_t to t_flag. |
42 |
|
|
* @param n in {1,2,4} |
43 |
|
|
* @return YYTD_DATA*. |
44 |
|
|
*/ |
45 |
|
|
#define BYTES2TFLAG(n)\ |
46 |
|
|
(((n) == sizeof(flex_int8_t))\ |
47 |
|
|
? YYTD_DATA8\ |
48 |
|
|
:(((n)== sizeof(flex_int16_t))\ |
49 |
|
|
? YYTD_DATA16\ |
50 |
|
|
: YYTD_DATA32)) |
51 |
|
|
|
52 |
|
|
/** Clear YYTD_DATA* bit flags |
53 |
|
|
* @return the flag with the YYTD_DATA* bits cleared |
54 |
|
|
*/ |
55 |
|
|
#define TFLAGS_CLRDATA(flg) ((flg) & ~(YYTD_DATA8 | YYTD_DATA16 | YYTD_DATA32)) |
56 |
|
|
|
57 |
|
|
int yytbl_write32 (struct yytbl_writer *wr, flex_uint32_t v); |
58 |
|
|
int yytbl_write16 (struct yytbl_writer *wr, flex_uint16_t v); |
59 |
|
|
int yytbl_write8 (struct yytbl_writer *wr, flex_uint8_t v); |
60 |
|
|
int yytbl_writen (struct yytbl_writer *wr, void *v, flex_int32_t len); |
61 |
|
|
static flex_int32_t yytbl_data_geti (const struct yytbl_data *tbl, int i); |
62 |
|
|
|
63 |
|
|
/** Initialize the table writer. |
64 |
|
|
* @param wr an uninitialized writer |
65 |
|
|
* @param the output file |
66 |
|
|
* @return 0 on success |
67 |
|
|
*/ |
68 |
|
|
int yytbl_writer_init (struct yytbl_writer *wr, FILE * out) |
69 |
|
|
{ |
70 |
|
|
wr->out = out; |
71 |
|
|
wr->total_written = 0; |
72 |
|
|
return 0; |
73 |
|
|
} |
74 |
|
|
|
75 |
|
|
/** Initialize a table header. |
76 |
|
|
* @param th The uninitialized structure |
77 |
|
|
* @param version_str the version string |
78 |
|
|
* @param name the name of this table set |
79 |
|
|
*/ |
80 |
|
|
int yytbl_hdr_init (struct yytbl_hdr *th, const char *version_str, |
81 |
|
|
const char *name) |
82 |
|
|
{ |
83 |
|
|
memset (th, 0, sizeof (struct yytbl_hdr)); |
84 |
|
|
|
85 |
|
|
th->th_magic = YYTBL_MAGIC; |
86 |
|
|
th->th_hsize = 14 + strlen (version_str) + 1 + strlen (name) + 1; |
87 |
|
|
th->th_hsize += yypad64 (th->th_hsize); |
88 |
|
|
th->th_ssize = 0; // Not known at this point. |
89 |
|
|
th->th_flags = 0; |
90 |
|
|
th->th_version = copy_string (version_str); |
91 |
|
|
th->th_name = copy_string (name); |
92 |
|
|
return 0; |
93 |
|
|
} |
94 |
|
|
|
95 |
|
|
/** Allocate and initialize a table data structure. |
96 |
|
|
* @param tbl a pointer to an uninitialized table |
97 |
|
|
* @param id the table identifier |
98 |
|
|
* @return 0 on success |
99 |
|
|
*/ |
100 |
|
|
int yytbl_data_init (struct yytbl_data *td, enum yytbl_id id) |
101 |
|
|
{ |
102 |
|
|
|
103 |
|
52 |
memset (td, 0, sizeof (struct yytbl_data)); |
104 |
|
26 |
td->td_id = id; |
105 |
|
26 |
td->td_flags = YYTD_DATA32; |
106 |
|
26 |
return 0; |
107 |
|
|
} |
108 |
|
|
|
109 |
|
|
/** Clean up table and data array. |
110 |
|
|
* @param td will be destroyed |
111 |
|
|
* @return 0 on success |
112 |
|
|
*/ |
113 |
|
|
int yytbl_data_destroy (struct yytbl_data *td) |
114 |
|
|
{ |
115 |
|
|
free(td->td_data); |
116 |
|
|
td->td_data = 0; |
117 |
|
|
free (td); |
118 |
|
|
return 0; |
119 |
|
|
} |
120 |
|
|
|
121 |
|
|
/** Write enough padding to bring the file pointer to a 64-bit boundary. */ |
122 |
|
|
static int yytbl_write_pad64 (struct yytbl_writer *wr) |
123 |
|
|
{ |
124 |
|
|
int pad, bwritten = 0; |
125 |
|
|
|
126 |
|
|
pad = yypad64 (wr->total_written); |
127 |
|
|
while (pad-- > 0) |
128 |
|
|
if (yytbl_write8 (wr, 0) < 0) |
129 |
|
|
return -1; |
130 |
|
|
else |
131 |
|
|
bwritten++; |
132 |
|
|
return bwritten; |
133 |
|
|
} |
134 |
|
|
|
135 |
|
|
/** write the header. |
136 |
|
|
* @param out the output stream |
137 |
|
|
* @param th table header to be written |
138 |
|
|
* @return -1 on error, or bytes written on success. |
139 |
|
|
*/ |
140 |
|
|
int yytbl_hdr_fwrite (struct yytbl_writer *wr, const struct yytbl_hdr *th) |
141 |
|
|
{ |
142 |
|
|
int sz, rv; |
143 |
|
|
int bwritten = 0; |
144 |
|
|
|
145 |
|
|
if (yytbl_write32 (wr, th->th_magic) < 0 |
146 |
|
|
|| yytbl_write32 (wr, th->th_hsize) < 0) |
147 |
|
|
flex_die (_("th_magic|th_hsize write32 failed")); |
148 |
|
|
bwritten += 8; |
149 |
|
|
|
150 |
|
|
if (fgetpos (wr->out, &(wr->th_ssize_pos)) != 0) |
151 |
|
|
flex_die (_("fgetpos failed")); |
152 |
|
|
|
153 |
|
|
if (yytbl_write32 (wr, th->th_ssize) < 0 |
154 |
|
|
|| yytbl_write16 (wr, th->th_flags) < 0) |
155 |
|
|
flex_die (_("th_ssize|th_flags write failed")); |
156 |
|
|
bwritten += 6; |
157 |
|
|
|
158 |
|
|
sz = strlen (th->th_version) + 1; |
159 |
|
|
if ((rv = yytbl_writen (wr, th->th_version, sz)) != sz) |
160 |
|
|
flex_die (_("th_version writen failed")); |
161 |
|
|
bwritten += rv; |
162 |
|
|
|
163 |
|
|
sz = strlen (th->th_name) + 1; |
164 |
|
|
if ((rv = yytbl_writen (wr, th->th_name, sz)) != sz) |
165 |
|
|
flex_die (_("th_name writen failed")); |
166 |
|
|
bwritten += rv; |
167 |
|
|
|
168 |
|
|
/* add padding */ |
169 |
|
|
if ((rv = yytbl_write_pad64 (wr)) < 0) |
170 |
|
|
flex_die (_("pad64 failed")); |
171 |
|
|
bwritten += rv; |
172 |
|
|
|
173 |
|
|
/* Sanity check */ |
174 |
|
|
if (bwritten != (int) th->th_hsize) |
175 |
|
|
flex_die (_("pad64 failed")); |
176 |
|
|
|
177 |
|
|
return bwritten; |
178 |
|
|
} |
179 |
|
|
|
180 |
|
|
|
181 |
|
|
/** Write this table. |
182 |
|
|
* @param out the file writer |
183 |
|
|
* @param td table data to be written |
184 |
|
|
* @return -1 on error, or bytes written on success. |
185 |
|
|
*/ |
186 |
|
|
int yytbl_data_fwrite (struct yytbl_writer *wr, struct yytbl_data *td) |
187 |
|
|
{ |
188 |
|
|
int rv; |
189 |
|
|
flex_int32_t bwritten = 0; |
190 |
|
|
flex_int32_t i, total_len; |
191 |
|
|
fpos_t pos; |
192 |
|
|
|
193 |
|
|
if ((rv = yytbl_write16 (wr, td->td_id)) < 0) |
194 |
|
|
return -1; |
195 |
|
|
bwritten += rv; |
196 |
|
|
|
197 |
|
|
if ((rv = yytbl_write16 (wr, td->td_flags)) < 0) |
198 |
|
|
return -1; |
199 |
|
|
bwritten += rv; |
200 |
|
|
|
201 |
|
|
if ((rv = yytbl_write32 (wr, td->td_hilen)) < 0) |
202 |
|
|
return -1; |
203 |
|
|
bwritten += rv; |
204 |
|
|
|
205 |
|
|
if ((rv = yytbl_write32 (wr, td->td_lolen)) < 0) |
206 |
|
|
return -1; |
207 |
|
|
bwritten += rv; |
208 |
|
|
|
209 |
|
|
total_len = yytbl_calc_total_len (td); |
210 |
|
|
for (i = 0; i < total_len; i++) { |
211 |
|
|
switch (YYTDFLAGS2BYTES (td->td_flags)) { |
212 |
|
|
case sizeof (flex_int8_t): |
213 |
|
|
rv = yytbl_write8 (wr, yytbl_data_geti (td, i)); |
214 |
|
|
break; |
215 |
|
|
case sizeof (flex_int16_t): |
216 |
|
|
rv = yytbl_write16 (wr, yytbl_data_geti (td, i)); |
217 |
|
|
break; |
218 |
|
|
case sizeof (flex_int32_t): |
219 |
|
|
rv = yytbl_write32 (wr, yytbl_data_geti (td, i)); |
220 |
|
|
break; |
221 |
|
|
default: |
222 |
|
|
flex_die (_("invalid td_flags detected")); |
223 |
|
|
} |
224 |
|
|
if (rv < 0) { |
225 |
|
|
flex_die (_("error while writing tables")); |
226 |
|
|
return -1; |
227 |
|
|
} |
228 |
|
|
bwritten += rv; |
229 |
|
|
} |
230 |
|
|
|
231 |
|
|
/* Sanity check */ |
232 |
|
|
if (bwritten != (int) (12 + total_len * YYTDFLAGS2BYTES (td->td_flags))) { |
233 |
|
|
flex_die (_("insanity detected")); |
234 |
|
|
return -1; |
235 |
|
|
} |
236 |
|
|
|
237 |
|
|
/* add padding */ |
238 |
|
|
if ((rv = yytbl_write_pad64 (wr)) < 0) { |
239 |
|
|
flex_die (_("pad64 failed")); |
240 |
|
|
return -1; |
241 |
|
|
} |
242 |
|
|
bwritten += rv; |
243 |
|
|
|
244 |
|
|
/* Now go back and update the th_hsize member */ |
245 |
|
|
if (fgetpos (wr->out, &pos) != 0 |
246 |
|
|
|| fsetpos (wr->out, &(wr->th_ssize_pos)) != 0 |
247 |
|
|
|| yytbl_write32 (wr, wr->total_written) < 0 |
248 |
|
|
|| fsetpos (wr->out, &pos)) { |
249 |
|
|
flex_die (_("get|set|fwrite32 failed")); |
250 |
|
|
return -1; |
251 |
|
|
} |
252 |
|
|
else |
253 |
|
|
/* Don't count the int we just wrote. */ |
254 |
|
|
wr->total_written -= sizeof (flex_int32_t); |
255 |
|
|
return bwritten; |
256 |
|
|
} |
257 |
|
|
|
258 |
|
|
/** Write n bytes. |
259 |
|
|
* @param wr the table writer |
260 |
|
|
* @param v data to be written |
261 |
|
|
* @param len number of bytes |
262 |
|
|
* @return -1 on error. number of bytes written on success. |
263 |
|
|
*/ |
264 |
|
|
int yytbl_writen (struct yytbl_writer *wr, void *v, flex_int32_t len) |
265 |
|
|
{ |
266 |
|
|
int rv; |
267 |
|
|
|
268 |
|
|
rv = fwrite (v, 1, len, wr->out); |
269 |
|
|
if (rv != len) |
270 |
|
|
return -1; |
271 |
|
|
wr->total_written += len; |
272 |
|
|
return len; |
273 |
|
|
} |
274 |
|
|
|
275 |
|
|
/** Write four bytes in network byte order |
276 |
|
|
* @param wr the table writer |
277 |
|
|
* @param v a dword in host byte order |
278 |
|
|
* @return -1 on error. number of bytes written on success. |
279 |
|
|
*/ |
280 |
|
|
int yytbl_write32 (struct yytbl_writer *wr, flex_uint32_t v) |
281 |
|
|
{ |
282 |
|
|
flex_uint32_t vnet; |
283 |
|
|
size_t bytes, rv; |
284 |
|
|
|
285 |
|
|
vnet = htonl (v); |
286 |
|
|
bytes = sizeof (flex_uint32_t); |
287 |
|
|
rv = fwrite (&vnet, bytes, 1, wr->out); |
288 |
|
|
if (rv != 1) |
289 |
|
|
return -1; |
290 |
|
|
wr->total_written += bytes; |
291 |
|
|
return bytes; |
292 |
|
|
} |
293 |
|
|
|
294 |
|
|
/** Write two bytes in network byte order. |
295 |
|
|
* @param wr the table writer |
296 |
|
|
* @param v a word in host byte order |
297 |
|
|
* @return -1 on error. number of bytes written on success. |
298 |
|
|
*/ |
299 |
|
|
int yytbl_write16 (struct yytbl_writer *wr, flex_uint16_t v) |
300 |
|
|
{ |
301 |
|
|
flex_uint16_t vnet; |
302 |
|
|
size_t bytes, rv; |
303 |
|
|
|
304 |
|
|
vnet = htons (v); |
305 |
|
|
bytes = sizeof (flex_uint16_t); |
306 |
|
|
rv = fwrite (&vnet, bytes, 1, wr->out); |
307 |
|
|
if (rv != 1) |
308 |
|
|
return -1; |
309 |
|
|
wr->total_written += bytes; |
310 |
|
|
return bytes; |
311 |
|
|
} |
312 |
|
|
|
313 |
|
|
/** Write a byte. |
314 |
|
|
* @param wr the table writer |
315 |
|
|
* @param v the value to be written |
316 |
|
|
* @return -1 on error. number of bytes written on success. |
317 |
|
|
*/ |
318 |
|
|
int yytbl_write8 (struct yytbl_writer *wr, flex_uint8_t v) |
319 |
|
|
{ |
320 |
|
|
size_t bytes, rv; |
321 |
|
|
|
322 |
|
|
bytes = sizeof (flex_uint8_t); |
323 |
|
|
rv = fwrite (&v, bytes, 1, wr->out); |
324 |
|
|
if (rv != 1) |
325 |
|
|
return -1; |
326 |
|
|
wr->total_written += bytes; |
327 |
|
|
return bytes; |
328 |
|
|
} |
329 |
|
|
|
330 |
|
|
/** Extract data element [i] from array data tables treated as a single flat array of integers. |
331 |
|
|
* Be careful for 2-dimensional arrays or for YYTD_ID_TRANSITION, which is an array |
332 |
|
|
* of structs. |
333 |
|
|
* @param tbl data table |
334 |
|
|
* @param i index into array. |
335 |
|
|
* @return data[i] |
336 |
|
|
*/ |
337 |
|
|
static flex_int32_t yytbl_data_geti (const struct yytbl_data *tbl, int i) |
338 |
|
|
{ |
339 |
|
|
|
340 |
|
|
switch (YYTDFLAGS2BYTES (tbl->td_flags)) { |
341 |
|
|
case sizeof (flex_int8_t): |
342 |
|
|
return ((flex_int8_t *) (tbl->td_data))[i]; |
343 |
|
|
case sizeof (flex_int16_t): |
344 |
|
|
return ((flex_int16_t *) (tbl->td_data))[i]; |
345 |
|
|
case sizeof (flex_int32_t): |
346 |
|
|
return ((flex_int32_t *) (tbl->td_data))[i]; |
347 |
|
|
default: |
348 |
|
|
flex_die (_("invalid td_flags detected")); |
349 |
|
|
break; |
350 |
|
|
} |
351 |
|
|
return 0; |
352 |
|
|
} |
353 |
|
|
|
354 |
|
|
/** Set data element [i] in array data tables treated as a single flat array of integers. |
355 |
|
|
* Be careful for 2-dimensional arrays or for YYTD_ID_TRANSITION, which is an array |
356 |
|
|
* of structs. |
357 |
|
|
* @param tbl data table |
358 |
|
|
* @param i index into array. |
359 |
|
|
* @param newval new value for data[i] |
360 |
|
|
*/ |
361 |
|
|
static void yytbl_data_seti (const struct yytbl_data *tbl, int i, |
362 |
|
|
flex_int32_t newval) |
363 |
|
|
{ |
364 |
|
|
|
365 |
|
|
switch (YYTDFLAGS2BYTES (tbl->td_flags)) { |
366 |
|
|
case sizeof (flex_int8_t): |
367 |
|
|
((flex_int8_t *) (tbl->td_data))[i] = (flex_int8_t) newval; |
368 |
|
|
break; |
369 |
|
|
case sizeof (flex_int16_t): |
370 |
|
|
((flex_int16_t *) (tbl->td_data))[i] = (flex_int16_t) newval; |
371 |
|
|
break; |
372 |
|
|
case sizeof (flex_int32_t): |
373 |
|
|
((flex_int32_t *) (tbl->td_data))[i] = (flex_int32_t) newval; |
374 |
|
|
break; |
375 |
|
|
default: |
376 |
|
|
flex_die (_("invalid td_flags detected")); |
377 |
|
|
break; |
378 |
|
|
} |
379 |
|
|
} |
380 |
|
|
|
381 |
|
|
/** Calculate the number of bytes needed to hold the largest |
382 |
|
|
* absolute value in this data array. |
383 |
|
|
* @param tbl the data table |
384 |
|
|
* @return sizeof(n) where n in {flex_int8_t, flex_int16_t, flex_int32_t} |
385 |
|
|
*/ |
386 |
|
|
static size_t min_int_size (struct yytbl_data *tbl) |
387 |
|
|
{ |
388 |
|
|
flex_uint32_t i, total_len; |
389 |
|
|
flex_int32_t max = 0; |
390 |
|
|
|
391 |
|
|
total_len = yytbl_calc_total_len (tbl); |
392 |
|
|
|
393 |
|
|
for (i = 0; i < total_len; i++) { |
394 |
|
|
flex_int32_t n; |
395 |
|
|
|
396 |
|
|
n = abs (yytbl_data_geti (tbl, i)); |
397 |
|
|
|
398 |
|
|
if (n > max) |
399 |
|
|
max = n; |
400 |
|
|
} |
401 |
|
|
|
402 |
|
|
if (max <= INT8_MAX) |
403 |
|
|
return sizeof (flex_int8_t); |
404 |
|
|
else if (max <= INT16_MAX) |
405 |
|
|
return sizeof (flex_int16_t); |
406 |
|
|
else |
407 |
|
|
return sizeof (flex_int32_t); |
408 |
|
|
} |
409 |
|
|
|
410 |
|
|
/** Transform data to smallest possible of (int32, int16, int8). |
411 |
|
|
* For example, we may have generated an int32 array due to user options |
412 |
|
|
* (e.g., %option align), but if the maximum value in that array |
413 |
|
|
* is 80 (for example), then we can serialize it with only 1 byte per int. |
414 |
|
|
* This is NOT the same as compressed DFA tables. We're just trying |
415 |
|
|
* to save storage space here. |
416 |
|
|
* |
417 |
|
|
* @param tbl the table to be compressed |
418 |
|
|
*/ |
419 |
|
|
void yytbl_data_compress (struct yytbl_data *tbl) |
420 |
|
|
{ |
421 |
|
|
flex_int32_t i, newsz, total_len; |
422 |
|
|
struct yytbl_data newtbl; |
423 |
|
|
|
424 |
|
|
yytbl_data_init (&newtbl, tbl->td_id); |
425 |
|
|
newtbl.td_hilen = tbl->td_hilen; |
426 |
|
|
newtbl.td_lolen = tbl->td_lolen; |
427 |
|
|
newtbl.td_flags = tbl->td_flags; |
428 |
|
|
|
429 |
|
|
newsz = min_int_size (tbl); |
430 |
|
|
|
431 |
|
|
|
432 |
|
|
if (newsz == (int) YYTDFLAGS2BYTES (tbl->td_flags)) |
433 |
|
|
/* No change in this table needed. */ |
434 |
|
|
return; |
435 |
|
|
|
436 |
|
|
if (newsz > (int) YYTDFLAGS2BYTES (tbl->td_flags)) { |
437 |
|
|
flex_die (_("detected negative compression")); |
438 |
|
|
return; |
439 |
|
|
} |
440 |
|
|
|
441 |
|
|
total_len = yytbl_calc_total_len (tbl); |
442 |
|
|
newtbl.td_data = calloc (total_len, newsz); |
443 |
|
|
newtbl.td_flags = |
444 |
|
|
TFLAGS_CLRDATA (newtbl.td_flags) | BYTES2TFLAG (newsz); |
445 |
|
|
|
446 |
|
|
for (i = 0; i < total_len; i++) { |
447 |
|
|
flex_int32_t g; |
448 |
|
|
|
449 |
|
|
g = yytbl_data_geti (tbl, i); |
450 |
|
|
yytbl_data_seti (&newtbl, i, g); |
451 |
|
|
} |
452 |
|
|
|
453 |
|
|
|
454 |
|
|
/* Now copy over the old table */ |
455 |
|
|
free (tbl->td_data); |
456 |
|
|
*tbl = newtbl; |
457 |
|
|
} |