Coverage Report

Created: 2026-06-14 06:59

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