increase benchmark duration
[rofl0r-stringswitch.git] / stringswitch_gen.c
blob7db44ed4171cfe679eb8ec77e4217b3b81e8047c
1 /*
2 Copyright (C) 2011 rofl0r
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include "../lib/include/strlib.h"
20 #include "../lib/include/stringptrlist.h"
21 #include "../lib/include/stringptr.h"
22 #include "../lib/include/hashlist.h"
23 #include "../lib/include/fileparser.h"
24 #include "../lib/include/logger.h"
25 #include "../lib/include/strswitch.h"
26 #include <fcntl.h>
27 #include <stddef.h>
28 #include <unistd.h>
29 #include <stdlib.h>
31 typedef sblist size_t_ptrlist;
33 typedef struct {
34 size_t len;
35 stringptrlist* list;
36 } hashrec;
38 typedef struct {
39 stringptr* id;
40 uint32_t hash;
41 hashlist* h;
42 } sectionrec;
44 static uint32_t daliashash_sp(stringptr* str) {
45 uint_fast32_t h = 0;
46 unsigned char* s = (void*) str->ptr;
47 size_t size = str->size;
48 while (size) {
49 h = 16*h + *s++;
50 h ^= h>>24 & 0xf0;
51 size--;
53 return h & 0xfffffff;
56 stringptr normalize(stringptr* id, char* workbuf, ssize_t buflen) {
57 size_t i;
58 size_t os = buflen;
59 char hexbuf[5];
60 stringptr res = {workbuf, 0};
62 for(i = 0; i < id->size; i++) {
63 switch(id->ptr[i]) {
64 case STRSWITCH_ALPHA:
65 buflen--;
66 if(buflen <= 0) goto err;
67 *workbuf++ = id->ptr[i];
68 break;
69 default:
70 buflen -= 4;
71 if(buflen <= 0) goto err;
72 ulz_snprintf(hexbuf, 5, "0x%.2X", id->ptr[i]);
73 memcpy(workbuf, hexbuf, 4);
74 workbuf += 4;
75 break;
79 if(buflen > 0)
80 *workbuf = 0;
82 res.size = os - buflen;
84 out:
85 return res;
86 err:
87 res.ptr = NULL;
88 goto out;
91 typedef sblist variant_info_list;
93 typedef struct {
94 int c;
95 size_t_ptrlist* members;
96 variant_info_list* variants;
97 } variant_info;
100 variant_info* find_variant(sblist* variants, int c) {
101 size_t i;
102 variant_info* res;
103 for(i = 0; i < sblist_getsize(variants); i++) {
104 res = sblist_get(variants, i);
105 if(res->c == c) return res;
107 return NULL;
110 int in_member_list(variant_info* variant, size_t index) {
111 size_t i, *r;
112 for(i = 0; i < sblist_getsize(variant->members); i++) {
113 r = sblist_get(variant->members, i);
114 if (*r == index) return 1;
116 return 0;
119 void write_enum(int outfd, sectionrec* sref) {
120 hashlist* h;
121 hashlist_iterator iter2;
122 hashrec *href;
123 stringptrlist* newlist;
124 size_t i;
125 stringptr *entry, normalized;
126 char norm_buf[1024];
127 char buf[1024];
129 log_put(outfd, VARISL("typedef enum {\n\tstringswitch_enumerator_default_member_name("), VARIS(sref->id), VARISL("),"), NULL);
131 h = sref->h;
132 hashlist_iterator_init(&iter2);
133 while((href = hashlist_next(h, &iter2))) {
134 newlist = href->list;
135 for(i = 0; i < stringptrlist_getsize(newlist); i++) {
136 entry = stringptrlist_get(newlist, i);
137 log_put(1, VARISL("id "), VARIS(sref->id), VARISL(", entry: "), VARII((int) entry->size), VARISL(", "), VARIS(entry), NULL);
138 normalized = normalize(entry, norm_buf, sizeof(norm_buf));
139 ulz_snprintf(buf, sizeof(buf), "\tstringswitch_enumerator_member_name(%s, %s),\n", sref->id->ptr, normalized.ptr);
140 log_putc(outfd, buf);
144 log_put(outfd, VARISL("} stringswitch_enumerator_name("), VARIS(sref->id), VARISL(");\n"), NULL);
148 // initialise with members list, containing all ids
149 variant_info_list* get_variants(size_t_ptrlist* members, stringptr* last_prefix, stringptrlist* block_strings, size_t block_len) {
150 size_t i;
151 variant_info vr, *vp;
152 stringptr* next_prefix, *act;
153 size_t* id;
154 if(last_prefix->size == block_len) return NULL;
155 variant_info_list* res = NULL;
156 stringptr temp;
157 char c;
159 for(i = 0; i < sblist_getsize(members); i++) {
160 id = sblist_get(members, i);
161 act = stringptrlist_get(block_strings, *id);
162 if(last_prefix->size == 0 || stringptr_here(act, 0, last_prefix)) {
163 if(!res) res = sblist_new(sizeof(variant_info), 16);
164 if(!(vp = find_variant(res, act->ptr[last_prefix->size]))) {
165 vr.c = act->ptr[last_prefix->size];
166 vr.members = sblist_new(sizeof(size_t), stringptrlist_getsize(members));
167 vr.variants = NULL;
168 sblist_add(res, &vr);
169 vp = find_variant(res, vr.c);
171 sblist_add(vp->members, id);
174 if(res) {
175 for (i = 0; i < sblist_getsize(res); i++) {
176 vp = sblist_get(res, i);
177 c = vp->c;
178 temp.size = 1;
179 temp.ptr = &c;
180 next_prefix = stringptr_concat(last_prefix, &temp, NULL);
181 vp->variants = get_variants(vp->members, next_prefix, block_strings, block_len);
184 return res;
187 void print_level_tabs(int fd, size_t level) {
188 size_t i;
189 for (i = 0; i < level + 3; i++)
190 ulz_fprintf(fd, "\t");
193 void dump_variants(int outfd, stringptr* section_name, variant_info_list* variants,
194 stringptrlist* block_strings, size_t level, size_t previous_membercount, ssize_t backindent) {
195 if(variants == NULL) return;
196 variant_info* vp;
197 stringptr* act, normalized;
198 char norm_buf[1024];
199 size_t membercount;
200 size_t* id;
201 int switched = 0;
202 sblist_iter(variants, vp) {
203 membercount = sblist_getsize(vp->members);
204 if(membercount == previous_membercount) {
205 print_level_tabs(outfd, level - backindent);
206 ulz_fprintf(outfd, "if(str[%zu]!='%c') goto main_default;\n", level, vp->c);
207 backindent++;
208 } else {
209 if(!switched) {
210 print_level_tabs(outfd, level - backindent);
211 ulz_fprintf(outfd, "switch(str[%zu]) {\n", level);
212 switched = 1;
213 backindent--;
215 print_level_tabs(outfd, level - backindent);
216 ulz_fprintf(outfd, "case '%c':\n", vp->c);
218 if(!vp->variants) {
219 print_level_tabs(outfd, level - backindent);
220 id = sblist_get(vp->members, 0);
221 act = sblist_get(block_strings, *id);
222 normalized = normalize(act, norm_buf, sizeof(norm_buf));
223 log_put(outfd, VARISL("\treturn stringswitch_enumerator_member_name("),
224 VARIS(section_name), VARISL(", "), VARIS(&normalized), VARISL(");"), NULL);
226 dump_variants(outfd, section_name, vp->variants, block_strings, level+1, membercount, backindent);
228 if(switched) {
229 print_level_tabs(outfd, level - backindent);
230 ulz_fprintf(outfd, "default: goto main_default;\n");
231 backindent++;
232 print_level_tabs(outfd, level - backindent);
233 ulz_fprintf(outfd, "}\n");
237 void free_variants(variant_info_list* v) {
238 (void) v;
239 // TODO
242 void write_length_block(int outfd, hashrec* block_href, stringptr* section_name) {
243 stringptrlist* newlist;
244 size_t i;
245 stringptr temp;
248 newlist = block_href->list;
249 log_put(outfd, VARISL("\t\tcase "),VARII((int) block_href->len), VARISL(":"), NULL);
251 size_t_ptrlist* initial_members = sblist_new(sizeof(size_t), stringptrlist_getsize(block_href->list));
252 temp.size = 0;
253 temp.ptr = NULL;
255 for(i = 0; i < stringptrlist_getsize(block_href->list); i++)
256 sblist_add(initial_members, &i);
258 variant_info_list* variants = get_variants(initial_members, &temp, block_href->list, block_href->len);
259 dump_variants(outfd, section_name, variants, block_href->list, 0, stringptrlist_getsize(block_href->list), 0);
261 sblist_free(initial_members);
262 free_variants(variants);
266 void codegen(hashlist* hashlists) {
267 sectionrec *sref;
268 hashrec *href;
270 hashlist_iterator iter, iter2;
271 hashlist_iterator_init(&iter);
272 int outfd = -1;
273 char buf[1024];
275 if(hashlists) while((sref = hashlist_next(hashlists, &iter))) {
277 // iterating through the "sections", that is each different strswitch usage
278 ulz_snprintf(buf, sizeof(buf), "stringswitch_impl_%s.c", sref->id->ptr);
280 outfd = open(buf, O_CREAT | O_TRUNC | O_WRONLY, 0660);
281 if(outfd == -1) {
282 log_putc(2, buf);
283 log_perror(" : couldnt open");
284 return;
286 log_put(1, VARISL("writing to "), VARIC(buf), NULL);
287 write_enum(outfd, sref);
289 log_put(outfd, VARISL("static int stringswitch_enumerator_eval_func("), VARIS(sref->id),
290 VARISL(") (char* str, size_t l) {"), NULL);
291 log_put(outfd, VARISL("\tswitch(l) {"), NULL);
294 hashlist_iterator_init(&iter2);
295 while((href = hashlist_next(sref->h, &iter2))) {
296 // iterating through the different length blocks in our switch statement
298 write_length_block(outfd, href, sref->id);
302 log_put(outfd, VARISL("\t\tdefault:\n\t\t\tmain_default:\n\t\t\treturn stringswitch_enumerator_default_member_name("),
303 VARIS(sref->id), VARISL(");\n\t}\n}"), NULL);
305 close(outfd);
310 void free_sections_hashlist(hashlist* l) {
311 // TODO ... free recursive
312 (void) l;
315 // we have a hashlist, "hashlists", in which we will insert one record of type
316 // sectionrec per different "section". a section is the identifier of a stringswitch statement.
317 // this rec contains a reference to another hashlist, which will contain one stringptrlist per
318 // length of the "add"ed entries.
320 static stringptr* command_prefix = SPL("//stringswitch_gen ");
321 hashlist* parse(char* file) {
322 hashlist *hashlists = NULL;
323 hashlist *h = NULL;
324 fileparser fb, *f = &fb;
325 stringptr line;
326 stringptr command;
327 stringptr param1, param2;
328 size_t x;
329 hashrec hr, *href;
330 sectionrec sr, *sref;
331 sblist* sbt;
332 stringptrlist* newlist;
333 uint32_t p1hash;
334 size_t i;
336 fileparser_open(f, file);
337 while(!fileparser_readline(f) && !fileparser_getline(f, &line)) {
338 if(stringptr_here(&line, 0, command_prefix)) {
340 command.ptr = line.ptr + command_prefix->size;
341 x = 0;
342 while(command_prefix->size + x < line.size &&
343 !(command.ptr[x] == ' ' || command.ptr[x] == 0)) x++;
344 command.size = x;
346 //log_puts(1, &command); log_putln(1);
348 param1.ptr = command.ptr + x + 1;
349 if(param1.ptr >= line.ptr + line.size) {
350 ulz_printf("no param1\n");
351 goto err;
353 x = 0;
354 while(command_prefix->size + command.size + x + 1 < line.size &&
355 !(param1.ptr[x] == ' ' || param1.ptr[x] == 0))
356 x++;
357 param1.size = x;
359 //log_puts(1, &param1); log_putln(1);
361 if(!hashlists)
362 hashlists = hashlist_new(64, sizeof(sectionrec));
364 p1hash = daliashash_sp(&param1);
366 h = NULL;
367 sbt = hashlist_get(hashlists, p1hash);
368 if(!sbt) {
369 insert_hashlist:
370 h = hashlist_new(64, sizeof(hashrec));
371 sr.h = h;
372 sr.hash = p1hash;
373 sr.id = stringptr_copy(&param1);
374 hashlist_add(hashlists, p1hash, &sr);
375 sbt = hashlist_get(hashlists, p1hash);
377 if(!h)
378 for(i = 0; i < sblist_getsize(sbt); i++) {
379 sref = sblist_get(sbt, i);
380 if(sref->hash == p1hash && EQ(&param1, sref->id)) {
381 h = sref->h;
382 break;
386 if(!h) goto insert_hashlist;
389 if(EQ(&command, SPL("add"))) {
391 param2.ptr = param1.ptr + x + 1;
393 if(param2.ptr >= line.ptr + line.size) {
394 ulz_printf("no param2\n");
395 goto err;
398 if(param2.ptr[0] != '"') {
399 ulz_printf("add parameter must be double-quoted\n");
400 goto err;
402 param2.ptr++;
404 if(param2.ptr >= line.ptr + line.size) {
405 ulz_printf("no param2\n");
406 goto err;
409 x = 0;
410 while(command_prefix->size + command.size + param1.size + x + 2 + 1 < line.size &&
411 !(param2.ptr[x] == '"' || param2.ptr[x] == 0)) x++;
412 param2.size = x;
414 //log_puts(1, &param2); log_putln(1);
416 newlist = NULL;
417 sbt = hashlist_get(h, param2.size);
418 if(!sbt) {
419 insert_stringptrlist:
420 newlist = stringptrlist_new(4);
421 hr.len = param2.size;
422 hr.list = newlist;
423 hashlist_add(h, param2.size, &hr);
424 sbt = hashlist_get(h, param2.size);
426 if(!newlist)
427 for(i = 0; i < sblist_getsize(sbt); i++) {
428 href = sblist_get(sbt, i);
429 if(href->len == param2.size) {
430 newlist = href->list;
431 break;
434 if(!newlist) goto insert_stringptrlist;
435 stringptrlist_add(newlist, stringptr_strdup(&param2), param2.size);
436 } else {
437 #if 0
438 param2.size = 0;
439 param2.ptr = 0;
440 #else
441 log_put(1, VARISL("the \"add\" command needs an additional parameter embedded in double quotes!"), NULL);
442 goto err;
443 #endif
448 goto out;
449 err:
450 if(hashlists) free_sections_hashlist(hashlists);
451 hashlists = NULL;
452 out:
453 fileparser_close(f);
455 return hashlists;
458 int main(int argc, char** argv) {
459 int i;
460 hashlist* sections;
461 ulz_printf("stringswitch generator 1.0 by rofl0r\n");
462 ulz_printf("^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~\n");
463 if(argc == 1) {
464 ulz_printf("pass one or more filenames containing stringswitch directives embedded as C99 comments!\n");
465 return 1;
467 for(i = 1; i < argc; i++) {
468 if(access(argv[i], R_OK) != -1) {
469 sections = parse(argv[i]);
470 if(!sections) return 1;
471 codegen(sections);
474 return 0;