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
55int     yytbl_write32 (struct yytbl_writer *wr, flex_uint32_t v);
56int     yytbl_write16 (struct yytbl_writer *wr, flex_uint16_t v);
57int     yytbl_write8 (struct yytbl_writer *wr, flex_uint8_t v);
58int     yytbl_writen (struct yytbl_writer *wr, void *v, int len);
59static flex_int32_t yytbl_data_geti (const struct yytbl_data *tbl, int i);
60/* XXX Not used
61static 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 */
71int 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 */
83int 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 */
103int 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 */
116int 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. */
125static 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 */
143int 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 */
189int 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 */
267int 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 */
283int 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 */
302int 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 */
321int 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 */
343static 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 */
379static 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 */
403static 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 */
428static 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 */
461void 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