//
// cssmin.c
//
// (c) 2009 TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
//
#include "cssmin.h"
#define last l
#define undo (ungetc(n, stream), n)
#define next (n = fgetc(stream), undo)
#define buffer (buf[bufpos++] = c)
#define end (buf[bufpos] = '\0')
#define out (++nchars_out, putchar(c))
#define input (l = c, c = fgetc(stream))
#define stat(S, ...) fprintf(stderr, S "\n", __VA_ARGS__);
#define option(S) (strcmp(argv[1], S) == 0)
static int
minify(FILE *stream) {
clock_t start = clock();
char c, l, n, buf[CSSMIN_BUFFER_MAX];
int bufpos = 0, inblock = 0, nchars = 0, nchars_out = 0;
start:
switch (++nchars, input) {
case '\n': goto start;
case '{' : goto block;
case '}' : goto blockend;
case ' ' : goto space;
case '"' : goto string;
case EOF : goto finish;
default : out;
}
goto start;
block:
out; inblock = 1;
goto start;
blockend:
out; inblock = 0;
goto start;
string:
out;
if (input != '"') goto string;
else out;
goto start;
space:
if ((isalpha(last) && isalpha(next)) || // foo bar
(isalpha(last) && isdigit(next)) || // 1px 0
(isalpha(last) && next == '"') || // 1px "solid"
(last == '"' && !ispunct(next)) || // "solid" red
(!inblock && next == ':') || // foo :last-child
isdigit(last) || // 0 0
(next == '*' || last == '*')) out; // foo * bar
goto start;
finish:
stat("bytes: %d", nchars);
stat("bytes saved: %d", nchars - nchars_out);
stat("duration: %0.5f milliseconds", (float) (clock() - start) / CLOCKS_PER_SEC);
return 0;
}
static int
usage() {
printf("cssmin: %s\n"
"usage: cssmin < in.css > out.min.css\n", CSSMIN_VERSION);
exit(0);
}
int
main(int argc, char **argv) {
if (argc > 1)
if (option("--help")) usage();
else if (option("--version")) puts(CSSMIN_VERSION), exit(0);
return minify(stdin);
}
Refactorings
No refactoring yet !
Ants
December 1, 2009, December 01, 2009 23:07, permalink
Nice! A state machine implemented within a single function with the use of goto's. :-)
The buf stack variable looks unused.
The next macro looks awfully inefficient doing a fgetc() and then followed by an ungetc(), especially in the handler for 'space'. Why not just peek at the next character once and store that in a variable? Or even better read chunks of characters into the unused buf variable and do the scanning of the current character there so that you don't have the I/O overhead?
Tj Holowaychuk
December 2, 2009, December 02, 2009 00:47, permalink
my bad, think your right. The macros were from another project that actually needed to buffer strings. I previously thought that fgetc() did some internal stream buffering, but I could not find the source or much info but yeah wow in my other project after just using fread() its a huge huge performance gain
Tj Holowaychuk
December 2, 2009, December 02, 2009 00:49, permalink
I like keeping the state machines really simple, all the local vars kinda suck though, limited to macros
Repo is here http://github.com/visionmedia/cssmin looking for any suggestions