1.1 --- a/sourcepawn/compiler/scpack.c Fri Mar 28 06:21:34 2008 +0000
1.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
1.3 @@ -1,450 +0,0 @@
1.4 -/* compress.c -- Byte Pair Encoding compression */
1.5 -/* Copyright 1996 Philip Gage */
1.6 -
1.7 -/* This program appeared in the September 1997 issue of
1.8 - * C/C++ Users Journal. The original source code may still
1.9 - * be found at the web site of the magazine (www.cuj.com).
1.10 - *
1.11 - * It has been modified by me (Thiadmer Riemersma) to
1.12 - * compress only a section of the input file and to store
1.13 - * the compressed output along with the input as "C" strings.
1.14 - *
1.15 - * Compiling instructions:
1.16 - * Borland C++ 16-bit (large memory model is required):
1.17 - * bcc -ml scpack.c
1.18 - *
1.19 - * Watcom C/C++ 32-bit:
1.20 - * wcl386 scpack.c
1.21 - *
1.22 - * GNU C (Linux), 32-bit:
1.23 - * gcc scpack.c -o scpack
1.24 - */
1.25 -
1.26 -#include <assert.h>
1.27 -#include <limits.h>
1.28 -#include <stdio.h>
1.29 -#include <stdlib.h>
1.30 -#include <string.h>
1.31 -
1.32 -#if UINT_MAX > 0xFFFFU
1.33 - #define MAXSIZE 1024*1024L
1.34 -#else
1.35 - #define MAXSIZE UINT_MAX /* Input file buffer size */
1.36 -#endif
1.37 -#define HASHSIZE 8192 /* Hash table size, power of 2 */
1.38 -#define THRESHOLD 3 /* Increase for speed, min 3 */
1.39 -
1.40 -#define START_TOKEN "#ifdef SCPACK" /* start reading the buffer here */
1.41 -#define NAME_TOKEN "#define SCPACK_TABLE"
1.42 -#define SEP_TOKEN "#define SCPACK_SEPARATOR"
1.43 -#define TERM_TOKEN "#define SCPACK_TERMINATOR"
1.44 -#define TEMPFILE "~SCPACK.TMP"
1.45 -static char tablename[32+1] = "scpack_table";
1.46 -static char separator[16]=",";
1.47 -static char terminator[16]="";
1.48 -
1.49 -int compress(unsigned char *buffer, unsigned buffersize, unsigned char pairtable[128][2])
1.50 -{
1.51 - unsigned char *left, *right, *count;
1.52 - unsigned char a, b, bestcount;
1.53 - unsigned i, j, index, bestindex, code=128;
1.54 -
1.55 - /* Dynamically allocate buffers and check for errors */
1.56 - left = (unsigned char *)malloc(HASHSIZE);
1.57 - right = (unsigned char *)malloc(HASHSIZE);
1.58 - count = (unsigned char *)malloc(HASHSIZE);
1.59 - if (left==NULL || right==NULL || count==NULL) {
1.60 - printf("Error allocating memory\n");
1.61 - exit(1);
1.62 - }
1.63 -
1.64 - /* Check for errors */
1.65 - for (i=0; i<buffersize; i++)
1.66 - if (buffer[i] > 127) {
1.67 - printf("This program works only on text files (7-bit ASCII)\n");
1.68 - exit(1);
1.69 - }
1.70 -
1.71 - memset(pairtable, 0, 128*2*sizeof(char));
1.72 -
1.73 - do { /* Replace frequent pairs with bytes 128..255 */
1.74 -
1.75 - /* Enter counts of all byte pairs into hash table */
1.76 - memset(count,0,HASHSIZE);
1.77 - for (i=0; i<buffersize-1; i++) {
1.78 - a = buffer[i];
1.79 - b = buffer[i+1];
1.80 - /* ignore any pair with a '\0' */
1.81 - if (a == 0 || b == 0)
1.82 - continue;
1.83 - index = (a ^ (b << 6)) & (HASHSIZE-1);
1.84 - while ((left[index] != a || right[index] != b) &&
1.85 - count[index] != 0)
1.86 - index = (index + 1) & (HASHSIZE-1);
1.87 - left[index] = a;
1.88 - right[index] = b;
1.89 - if (count[index] < 255)
1.90 - count[index] += (unsigned char)1;
1.91 - }
1.92 -
1.93 - /* Search hash table for most frequent pair */
1.94 - bestcount = THRESHOLD - 1;
1.95 - for (i=0; i<HASHSIZE; i++) {
1.96 - if (count[i] > bestcount) {
1.97 - bestcount = count[i];
1.98 - bestindex = i;
1.99 - }
1.100 - }
1.101 -
1.102 - /* Compress if enough occurrences of pair */
1.103 - if (bestcount >= THRESHOLD) {
1.104 -
1.105 - /* Add pair to table using code as index */
1.106 - a = pairtable[code-128][0] = left[bestindex];
1.107 - b = pairtable[code-128][1] = right[bestindex];
1.108 -
1.109 - /* Replace all pair occurrences with unused byte */
1.110 - for (i=0, j=0; i<buffersize; i++, j++)
1.111 - if (a == buffer[i] && b == buffer[i+1]) {
1.112 - buffer[j] = (unsigned char)code;
1.113 - ++i;
1.114 - }
1.115 - else
1.116 - buffer[j] = buffer[i];
1.117 - buffersize = j;
1.118 - }
1.119 - else
1.120 - break;
1.121 - } while (++code < 255);
1.122 -
1.123 - /* done */
1.124 - free(left); free(right); free(count);
1.125 - return buffersize; /* return adjusted buffersize */
1.126 -}
1.127 -
1.128 -static int strmatch(char *str, char *token, int *indent)
1.129 -{
1.130 - int i = 0;
1.131 -
1.132 - /* skip whitespace */
1.133 - while (*str==' ' || *str=='\t') {
1.134 - str++;
1.135 - i++;
1.136 - } /* while */
1.137 - if (strncmp(str,token,strlen(token))!=0)
1.138 - return 0;
1.139 - if (indent != NULL)
1.140 - *indent = i;
1.141 - return 1;
1.142 -}
1.143 -
1.144 -static void check_if(char *str,int linenr)
1.145 -{
1.146 - if (strmatch(str,"#if",NULL)) {
1.147 - printf("Error: \"#if...\" preprocessor statement should not be in SCPACK section "
1.148 - "(line %d)\n", linenr);
1.149 - exit(1);
1.150 - } /* if */
1.151 -}
1.152 -
1.153 -static int check_tablename(char *str)
1.154 -{
1.155 - int i;
1.156 -
1.157 - if (strmatch(str,NAME_TOKEN,NULL)) {
1.158 - str += strlen(NAME_TOKEN);
1.159 - while (*str==' ' || *str=='\t')
1.160 - str++;
1.161 - for (i=0; i<(sizeof tablename - 1) && *str!='\0' && strchr(" \t\n",*str)==NULL; i++, str++)
1.162 - tablename[i] = *str;
1.163 - tablename[i] = '\0';
1.164 - return 1;
1.165 - } /* if */
1.166 - return 0;
1.167 -}
1.168 -
1.169 -static int check_separator(char *str)
1.170 -{
1.171 - int i;
1.172 -
1.173 - if (strmatch(str,SEP_TOKEN,NULL)) {
1.174 - str += strlen(SEP_TOKEN);
1.175 - while (*str==' ' || *str=='\t')
1.176 - str++;
1.177 - for (i=0; i<(sizeof separator - 1) && *str!='\0' && strchr(" \t\n",*str)==NULL; i++, str++)
1.178 - separator[i] = *str;
1.179 - separator[i] = '\0';
1.180 - return 1;
1.181 - } /* if */
1.182 -
1.183 - if (strmatch(str,TERM_TOKEN,NULL)) {
1.184 - str += strlen(TERM_TOKEN);
1.185 - while (*str==' ' || *str=='\t')
1.186 - str++;
1.187 - for (i=0; i<(sizeof terminator - 1) && *str!='\0' && strchr(" \t\n",*str)==NULL; i++, str++)
1.188 - terminator[i] = *str;
1.189 - terminator[i] = '\0';
1.190 - return 1;
1.191 - } /* if */
1.192 -
1.193 - return 0;
1.194 -}
1.195 -
1.196 -/* readbuffer
1.197 - * Reads in the input file and stores all strings in the
1.198 - * section between "#ifdef SCPACK" and "#else" in a buffer.
1.199 - * Only text that is between double quotes is added to the
1.200 - * buffer; the \" escape code is handled. Multiple strings
1.201 - * on one line are handled.
1.202 - */
1.203 -unsigned readbuffer(FILE *input, unsigned char *buffer)
1.204 -{
1.205 - char str[256];
1.206 - unsigned buffersize;
1.207 - int i,linenr;
1.208 -
1.209 - linenr=0;
1.210 - buffersize=0;
1.211 -
1.212 - rewind(input);
1.213 - while (!feof(input)) {
1.214 - while (fgets(str,sizeof str,input)!=NULL) {
1.215 - linenr++;
1.216 - check_tablename(str);
1.217 - check_separator(str);
1.218 - if (strmatch(str,START_TOKEN,NULL))
1.219 - break;
1.220 - } /* while */
1.221 - if (!strmatch(str,START_TOKEN,NULL))
1.222 - return buffersize; /* no (more) section found, quit */
1.223 -
1.224 - while (fgets(str,sizeof str,input)!=NULL) {
1.225 - linenr++;
1.226 - check_if(str,linenr);
1.227 - if (check_tablename(str))
1.228 - printf("Error: table name definition should not be in SCPACK section (line %d)\n", linenr);
1.229 - check_separator(str);
1.230 - if (strmatch(str,"#else",NULL))
1.231 - break; /* done */
1.232 - /* add to the buffer only what is between double quotes */
1.233 - i=0;
1.234 - do {
1.235 - while (str[i]!='\0' && str[i]!='"')
1.236 - i++;
1.237 - if (str[i]=='"') {
1.238 - /* we are in a string */
1.239 - i++;
1.240 - while (str[i]!='\0' && str[i]!='"') {
1.241 - /* handle escape sequences */
1.242 - if (str[i]=='\\') {
1.243 - i++;
1.244 - switch (str[i]) {
1.245 - case 'a': /* alarm */
1.246 - buffer[buffersize++]='\a';
1.247 - i++;
1.248 - break;
1.249 - case 'b': /* backspace */
1.250 - buffer[buffersize++]='\b';
1.251 - i++;
1.252 - break;
1.253 - case 'f': /* form feed */
1.254 - buffer[buffersize++]='\f';
1.255 - i++;
1.256 - break;
1.257 - case 'n': /* newline */
1.258 - buffer[buffersize++]='\n';
1.259 - i++;
1.260 - break;
1.261 - case 'r': /* carriage return */
1.262 - buffer[buffersize++]='\n';
1.263 - i++;
1.264 - break;
1.265 - case 't': /* tab */
1.266 - buffer[buffersize++]='\t';
1.267 - i++;
1.268 - break;
1.269 - case '\'':
1.270 - buffer[buffersize++]='\'';
1.271 - i++;
1.272 - break;
1.273 - case '"':
1.274 - buffer[buffersize++]='"';
1.275 - i++;
1.276 - break;
1.277 - default:
1.278 - // ??? octal character code escapes and hexadecimal escapes
1.279 - // not supported
1.280 - printf("Unknown escape sequence '\\%c' on line %d\n",
1.281 - str[i], linenr);
1.282 - } /* switch */
1.283 - } else {
1.284 - buffer[buffersize++]=str[i++];
1.285 - } /* if */
1.286 - } /* while */
1.287 - if (str[i]=='"') {
1.288 - buffer[buffersize++]='\0'; /* terminate each string */
1.289 - i++;
1.290 - } else {
1.291 - printf("Error: unterminated string on line %d\n",linenr);
1.292 - } /* if */
1.293 - } /* if */
1.294 - } while (str[i]!='\0');
1.295 - } /* while - in SCPACK section */
1.296 - /* put in another '\0' to terminate the section */
1.297 - buffer[buffersize++]='\0';
1.298 - } /* while - !feof(input) */
1.299 - return buffersize;
1.300 -}
1.301 -
1.302 -static void write_pairtable(FILE *output, unsigned char pairtable[128][2], char *tablename)
1.303 -{
1.304 - int i;
1.305 -
1.306 - /* dump the pair table */
1.307 - fprintf(output, "/*-*SCPACK start of pair table, do not change or remove this line */\n");
1.308 - fprintf(output, "unsigned char %s[][2] = {", tablename);
1.309 - for (i=0; i<128 && pairtable[i][0]!=0 && pairtable[i][1]!=0; i++) {
1.310 - if ((i % 16)==0)
1.311 - fprintf(output, "\n ");
1.312 - else
1.313 - fprintf(output, " ");
1.314 - fprintf(output, "{%d,%d}", pairtable[i][0], pairtable[i][1]);
1.315 - /* check if something follows this pair */
1.316 - if (i+1<128 && pairtable[i+1][0]!=0 && pairtable[i+1][1]!=0)
1.317 - fprintf(output, ",");
1.318 - } /* for */
1.319 - fprintf(output, "\n};\n");
1.320 - fprintf(output, "/*-*SCPACK end of pair table, do not change or remove this line */\n");
1.321 -}
1.322 -
1.323 -void writefile(FILE *input, FILE *output, unsigned char *buffer, unsigned buffersize, unsigned char pairtable[128][2])
1.324 -{
1.325 - char str[256];
1.326 - int insection, indent, needseparator;
1.327 - unsigned char *bufptr;
1.328 -
1.329 - bufptr = buffer;
1.330 - insection = 0;
1.331 -
1.332 - rewind(input);
1.333 - while (!feof(input)) {
1.334 - while (fgets(str,sizeof str,input)!=NULL) {
1.335 - fprintf(output,"%s",str);
1.336 - if (check_tablename(str)) {
1.337 - write_pairtable(output, pairtable, tablename);
1.338 - /* strip an existing pair table from the file */
1.339 - if (fgets(str,sizeof str,input)!=NULL) {
1.340 - if (strmatch(str,"/*-*SCPACK",NULL)) {
1.341 - while (fgets(str,sizeof str,input)!=NULL)
1.342 - if (strmatch(str,"/*-*SCPACK",NULL))
1.343 - break;
1.344 - } else {
1.345 - fprintf(output,"%s",str);
1.346 - } /* if */
1.347 - } /* if */
1.348 - } /* if */
1.349 - if (strmatch(str,START_TOKEN,NULL))
1.350 - insection = 1;
1.351 - if (insection && strmatch(str,"#else",NULL))
1.352 - break;
1.353 - } /* while */
1.354 - if (!strmatch(str,"#else",&indent))
1.355 - return; /* no (more) section found, quit */
1.356 - insection=0;
1.357 -
1.358 - /* dump the buffer as strings, separated with commas */
1.359 - needseparator = 0;
1.360 - while (*bufptr != '\0') {
1.361 - assert((unsigned)(bufptr-buffer) < buffersize);
1.362 - if (needseparator)
1.363 - fprintf(output, "%s\n",separator);
1.364 - fprintf(output, "%*c\"",indent+2,' ');
1.365 - /* loop over string */
1.366 - while (*bufptr != '\0') {
1.367 - if (*bufptr<' ' || *bufptr >= 128 || *bufptr == '"' || *bufptr == '\\')
1.368 - fprintf(output, "\\%03o", *bufptr);
1.369 - else
1.370 - fprintf(output, "%c", *bufptr);
1.371 - bufptr++;
1.372 - } /* while */
1.373 - fprintf(output, "\"");
1.374 - needseparator = 1;
1.375 - bufptr++; /* skip '\0' */
1.376 - } /* while */
1.377 - fprintf(output, "%s\n",terminator);
1.378 - bufptr++;
1.379 -
1.380 - /* skip the input file until the #endif section */
1.381 - while (fgets(str,sizeof str,input)!=NULL) {
1.382 - if (strmatch(str,"#endif",NULL)) {
1.383 - fprintf(output,"%s",str);
1.384 - break; /* done */
1.385 - } /* if */
1.386 - } /* while */
1.387 - } /* while - !feof(input) */
1.388 -}
1.389 -
1.390 -static void usage(void)
1.391 -{
1.392 - printf("Usage: scpack <filename> [output file]\n");
1.393 - exit(1);
1.394 -}
1.395 -
1.396 -int main(int argc, char **argv)
1.397 -{
1.398 - FILE *in, *out;
1.399 - unsigned char *buffer;
1.400 - unsigned buffersize, orgbuffersize;
1.401 - unsigned char pairtable[128][2];
1.402 -
1.403 - if (argc < 2 || argc > 3)
1.404 - usage();
1.405 - if ((in=fopen(argv[1],"rt"))==NULL) {
1.406 - printf("SCPACK: error opening input %s\n",argv[1]);
1.407 - usage();
1.408 - } /* if */
1.409 - if (argc == 2) {
1.410 - if ((out=fopen(TEMPFILE,"wt"))==NULL) {
1.411 - printf("SCPACK: error opening temporary file %s\n",TEMPFILE);
1.412 - usage();
1.413 - } /* if */
1.414 - } else {
1.415 - if ((out=fopen(argv[2],"wt"))==NULL) {
1.416 - printf("SCPACK: error opening output file %s\n",argv[2]);
1.417 - usage();
1.418 - } /* if */
1.419 - } /* if */
1.420 -
1.421 - buffer = (unsigned char *)malloc(MAXSIZE);
1.422 - if (buffer == NULL) {
1.423 - printf("SCPACK: error allocating memory\n");
1.424 - return 1;
1.425 - } /* if */
1.426 - /* 1. read the buffer
1.427 - * 2. compress the buffer
1.428 - * 3. copy the file, insert the compressed buffer
1.429 - */
1.430 - buffersize = readbuffer(in, buffer);
1.431 - orgbuffersize = buffersize;
1.432 - if (buffersize > 0) {
1.433 - buffersize = compress(buffer, buffersize, pairtable);
1.434 - writefile(in, out, buffer, buffersize, pairtable);
1.435 - printf("SCPACK: compression ratio: %ld%% (%d -> %d)\n",
1.436 - 100L-(100L*buffersize)/orgbuffersize, orgbuffersize, buffersize);
1.437 - } else {
1.438 - printf("SCPACK: no SCPACK section found, nothing to do\n");
1.439 - } /* if */
1.440 - fclose(out);
1.441 - fclose(in);
1.442 - /* let the new file replace the old file */
1.443 - if (buffersize == 0) {
1.444 - if (argc == 2)
1.445 - remove(TEMPFILE);
1.446 - else
1.447 - remove(argv[2]);
1.448 - } else if (argc == 2) {
1.449 - remove(argv[1]);
1.450 - rename(TEMPFILE,argv[1]);
1.451 - } /* if */
1.452 - return 0;
1.453 -}