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