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