Home | History | Annotate | Download | only in emugen
      1 /*
      2 * Copyright (C) 2011 The Android Open Source Project
      3 *
      4 * Licensed under the Apache License, Version 2.0 (the "License");
      5 * you may not use this file except in compliance with the License.
      6 * You may obtain a copy of the License at
      7 *
      8 * http://www.apache.org/licenses/LICENSE-2.0
      9 *
     10 * Unless required by applicable law or agreed to in writing, software
     11 * distributed under the License is distributed on an "AS IS" BASIS,
     12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 * See the License for the specific language governing permissions and
     14 * limitations under the License.
     15 */
     16 #include "ApiGen.h"
     17 #include "EntryPoint.h"
     18 #include <stdio.h>
     19 #include <stdlib.h>
     20 #include "strUtils.h"
     21 #include <errno.h>
     22 #include <sys/types.h>
     23 
     24 /* Define this to 1 to enable support for the 'isLarge' variable flag
     25  * that instructs the encoder to send large data buffers by a direct
     26  * write through the pipe (i.e. without copying it into a temporary
     27  * buffer. This has definite performance benefits when using a QEMU Pipe.
     28  *
     29  * Set to 0 otherwise.
     30  */
     31 #define WITH_LARGE_SUPPORT  1
     32 
     33 EntryPoint * ApiGen::findEntryByName(const std::string & name)
     34 {
     35     EntryPoint * entry = NULL;
     36 
     37     size_t n = this->size();
     38     for (size_t i = 0; i < n; i++) {
     39         if (at(i).name() == name) {
     40             entry = &(at(i));
     41             break;
     42         }
     43     }
     44     return entry;
     45 }
     46 
     47 void ApiGen::printHeader(FILE *fp) const
     48 {
     49     fprintf(fp, "// Generated Code - DO NOT EDIT !!\n");
     50     fprintf(fp, "// generated by 'emugen'\n");
     51 }
     52 
     53 int ApiGen::genProcTypes(const std::string &filename, SideType side)
     54 {
     55     FILE *fp = fopen(filename.c_str(), "wt");
     56     if (fp == NULL) {
     57         perror(filename.c_str());
     58         return -1;
     59     }
     60     printHeader(fp);
     61 
     62     const char* basename = m_basename.c_str();
     63 
     64     fprintf(fp, "#ifndef __%s_%s_proc_t_h\n", basename, sideString(side));
     65     fprintf(fp, "#define __%s_%s_proc_t_h\n", basename, sideString(side));
     66     fprintf(fp, "\n\n");
     67     fprintf(fp, "\n#include \"%s_types.h\"\n",basename);
     68     fprintf(fp, "#ifndef %s_APIENTRY\n",basename);
     69     fprintf(fp, "#define %s_APIENTRY \n",basename);
     70     fprintf(fp, "#endif\n");
     71 
     72 
     73     for (size_t i = 0; i < size(); i++) {
     74         EntryPoint *e = &at(i);
     75 
     76         fprintf(fp, "typedef ");
     77         e->retval().printType(fp);
     78         fprintf(fp, " (%s_APIENTRY *%s_%s_proc_t) (", basename, e->name().c_str(), sideString(side));
     79         if (side == CLIENT_SIDE) { fprintf(fp, "void * ctx"); }
     80         if (e->customDecoder() && side == SERVER_SIDE) { fprintf(fp, "void *ctx"); }
     81 
     82         VarsArray & evars = e->vars();
     83         size_t n = evars.size();
     84 
     85         for (size_t j = 0; j < n; j++) {
     86             if (!evars[j].isVoid()) {
     87                 if (j != 0 || side == CLIENT_SIDE || (side == SERVER_SIDE && e->customDecoder())) fprintf(fp, ", ");
     88                 evars[j].printType(fp);
     89             }
     90         }
     91         fprintf(fp, ");\n");
     92     }
     93     fprintf(fp, "\n\n#endif\n");
     94     return 0;
     95 }
     96 
     97 int ApiGen::genFuncTable(const std::string &filename, SideType side)
     98 {
     99     FILE *fp = fopen(filename.c_str(), "wt");
    100     if (fp == NULL) {
    101         perror(filename.c_str());
    102         return -1;
    103     }
    104     printHeader(fp);
    105 
    106     fprintf(fp, "#ifndef __%s_%s_ftable_t_h\n", m_basename.c_str(), sideString(side));
    107     fprintf(fp, "#define __%s_%s_ftable_t_h\n", m_basename.c_str(), sideString(side));
    108     fprintf(fp, "\n\n");
    109     fprintf(fp, "static struct _%s_funcs_by_name {\n", m_basename.c_str());
    110     fprintf(fp,
    111             "\tconst char *name;\n" \
    112             "\tvoid *proc;\n" \
    113             "} %s_funcs_by_name[] = {\n", m_basename.c_str());
    114 
    115 
    116     for (size_t i = 0; i < size(); i++) {
    117         EntryPoint *e = &at(i);
    118         if (e->notApi()) continue;
    119         fprintf(fp, "\t{\"%s\", (void*)%s},\n", e->name().c_str(), e->name().c_str());
    120     }
    121     fprintf(fp, "};\n");
    122     fprintf(fp, "static int %s_num_funcs = sizeof(%s_funcs_by_name) / sizeof(struct _%s_funcs_by_name);\n",
    123             m_basename.c_str(), m_basename.c_str(), m_basename.c_str());
    124     fprintf(fp, "\n\n#endif\n");
    125     return 0;
    126 }
    127 
    128 
    129 int ApiGen::genContext(const std::string & filename, SideType side)
    130 {
    131     FILE *fp = fopen(filename.c_str(), "wt");
    132     if (fp == NULL) {
    133         perror(filename.c_str());
    134         return -1;
    135     }
    136     printHeader(fp);
    137 
    138     fprintf(fp, "#ifndef __%s_%s_context_t_h\n", m_basename.c_str(), sideString(side));
    139     fprintf(fp, "#define __%s_%s_context_t_h\n", m_basename.c_str(), sideString(side));
    140 
    141     //  fprintf(fp, "\n#include \"%s_types.h\"\n", m_basename.c_str());
    142     fprintf(fp, "\n#include \"%s_%s_proc.h\"\n", m_basename.c_str(), sideString(side));
    143 
    144     StringVec & contextHeaders = side == CLIENT_SIDE ? m_clientContextHeaders : m_serverContextHeaders;
    145     for (size_t i = 0; i < contextHeaders.size(); i++) {
    146         fprintf(fp, "#include %s\n", contextHeaders[i].c_str());
    147     }
    148     fprintf(fp, "\n");
    149 
    150     fprintf(fp, "\nstruct %s_%s_context_t {\n\n", m_basename.c_str(), sideString(side));
    151     for (size_t i = 0; i < size(); i++) {
    152         EntryPoint *e = &at(i);
    153         fprintf(fp, "\t%s_%s_proc_t %s;\n", e->name().c_str(), sideString(side), e->name().c_str());
    154     }
    155     // accessors
    156     fprintf(fp, "\t//Accessors \n");
    157 
    158     for (size_t i = 0; i < size(); i++) {
    159         EntryPoint *e = &at(i);
    160         const char *n = e->name().c_str();
    161         const char *s = sideString(side);
    162         fprintf(fp, "\tvirtual %s_%s_proc_t set_%s(%s_%s_proc_t f) { %s_%s_proc_t retval = %s; %s = f; return retval;}\n", n, s, n, n, s, n, s,  n, n);
    163     }
    164 
    165     // virtual destructor
    166     fprintf(fp, "\t virtual ~%s_%s_context_t() {}\n", m_basename.c_str(), sideString(side));
    167     // accessor
    168     if (side == CLIENT_SIDE || side == WRAPPER_SIDE) {
    169         fprintf(fp, "\n\ttypedef %s_%s_context_t *CONTEXT_ACCESSOR_TYPE(void);\n",
    170                 m_basename.c_str(), sideString(side));
    171         fprintf(fp, "\tstatic void setContextAccessor(CONTEXT_ACCESSOR_TYPE *f);\n");
    172     }
    173 
    174     // init function
    175     fprintf(fp, "\tint initDispatchByName( void *(*getProc)(const char *name, void *userData), void *userData);\n");
    176 
    177     //client site set error virtual func
    178     if (side == CLIENT_SIDE) {
    179         fprintf(fp, "\tvirtual void setError(unsigned int  error){};\n");
    180         fprintf(fp, "\tvirtual unsigned int getError(){ return 0; };\n");
    181     }
    182 
    183     fprintf(fp, "};\n");
    184 
    185     fprintf(fp, "\n#endif\n");
    186     fclose(fp);
    187     return 0;
    188 }
    189 
    190 int ApiGen::genEntryPoints(const std::string & filename, SideType side)
    191 {
    192 
    193     if (side != CLIENT_SIDE && side != WRAPPER_SIDE) {
    194         fprintf(stderr, "Entry points are only defined for Client and Wrapper components\n");
    195         return -999;
    196     }
    197 
    198 
    199     FILE *fp = fopen(filename.c_str(), "wt");
    200     if (fp == NULL) {
    201         perror(filename.c_str());
    202         return errno;
    203     }
    204 
    205     printHeader(fp);
    206     fprintf(fp, "#include <stdio.h>\n");
    207     fprintf(fp, "#include <stdlib.h>\n");
    208     fprintf(fp, "#include \"%s_%s_context.h\"\n", m_basename.c_str(), sideString(side));
    209     fprintf(fp, "\n");
    210 
    211     fprintf(fp, "#ifndef GL_TRUE\n");
    212     fprintf(fp, "extern \"C\" {\n");
    213 
    214     for (size_t i = 0; i < size(); i++) {
    215         fprintf(fp, "\t"); at(i).print(fp, false); fprintf(fp, ";\n");
    216     }
    217     fprintf(fp, "};\n\n");
    218     fprintf(fp, "#endif\n");
    219 
    220     fprintf(fp, "#ifndef GET_CONTEXT\n");
    221     fprintf(fp, "static %s_%s_context_t::CONTEXT_ACCESSOR_TYPE *getCurrentContext = NULL;\n",
    222             m_basename.c_str(), sideString(side));
    223 
    224     fprintf(fp,
    225             "void %s_%s_context_t::setContextAccessor(CONTEXT_ACCESSOR_TYPE *f) { getCurrentContext = f; }\n",
    226             m_basename.c_str(), sideString(side));
    227     fprintf(fp, "#define GET_CONTEXT %s_%s_context_t * ctx = getCurrentContext() \n",
    228                 m_basename.c_str(), sideString(side));
    229     fprintf(fp, "#endif\n\n");
    230 
    231 
    232     for (size_t i = 0; i < size(); i++) {
    233         EntryPoint *e = &at(i);
    234         e->print(fp);
    235         fprintf(fp, "{\n");
    236         fprintf(fp, "\tGET_CONTEXT; \n");
    237 
    238         bool shouldReturn = !e->retval().isVoid();
    239         bool shouldCallWithContext = (side == CLIENT_SIDE);
    240         //param check
    241         if (shouldCallWithContext) {
    242             for (size_t j=0; j<e->vars().size(); j++) {
    243                 if (e->vars()[j].paramCheckExpression() != "")
    244                     fprintf(fp, "\t%s\n", e->vars()[j].paramCheckExpression().c_str());
    245             }
    246         }
    247         fprintf(fp, "\t %sctx->%s(%s",
    248                 shouldReturn ? "return " : "",
    249                 e->name().c_str(),
    250                 shouldCallWithContext ? "ctx" : "");
    251         size_t nvars = e->vars().size();
    252 
    253         for (size_t j = 0; j < nvars; j++) {
    254             if (!e->vars()[j].isVoid()) {
    255                 fprintf(fp, "%s %s",
    256                         j != 0 || shouldCallWithContext ? "," : "",
    257                         e->vars()[j].name().c_str());
    258             }
    259         }
    260         fprintf(fp, ");\n");
    261         fprintf(fp, "}\n\n");
    262     }
    263     fclose(fp);
    264     return 0;
    265 }
    266 
    267 
    268 int ApiGen::genOpcodes(const std::string &filename)
    269 {
    270     FILE *fp = fopen(filename.c_str(), "wt");
    271     if (fp == NULL) {
    272         perror(filename.c_str());
    273         return errno;
    274     }
    275 
    276     printHeader(fp);
    277     fprintf(fp, "#ifndef __GUARD_%s_opcodes_h_\n", m_basename.c_str());
    278     fprintf(fp, "#define __GUARD_%s_opcodes_h_\n\n", m_basename.c_str());
    279     for (size_t i = 0; i < size(); i++) {
    280         fprintf(fp, "#define OP_%s \t\t\t\t\t%u\n", at(i).name().c_str(), (unsigned int)i + m_baseOpcode);
    281     }
    282     fprintf(fp, "#define OP_last \t\t\t\t\t%u\n", (unsigned int)size() + m_baseOpcode);
    283     fprintf(fp,"\n\n#endif\n");
    284     fclose(fp);
    285     return 0;
    286 
    287 }
    288 int ApiGen::genAttributesTemplate(const std::string &filename )
    289 {
    290     FILE *fp = fopen(filename.c_str(), "wt");
    291     if (fp == NULL) {
    292         perror(filename.c_str());
    293         return -1;
    294     }
    295 
    296     for (size_t i = 0; i < size(); i++) {
    297         if (at(i).hasPointers()) {
    298             fprintf(fp, "#");
    299             at(i).print(fp);
    300             fprintf(fp, "%s\n\n", at(i).name().c_str());
    301         }
    302     }
    303     fclose(fp);
    304     return 0;
    305 }
    306 
    307 int ApiGen::genEncoderHeader(const std::string &filename)
    308 {
    309     FILE *fp = fopen(filename.c_str(), "wt");
    310     if (fp == NULL) {
    311         perror(filename.c_str());
    312         return -1;
    313     }
    314 
    315     printHeader(fp);
    316     std::string classname = m_basename + "_encoder_context_t";
    317 
    318     fprintf(fp, "\n#ifndef GUARD_%s\n", classname.c_str());
    319     fprintf(fp, "#define GUARD_%s\n\n", classname.c_str());
    320 
    321     fprintf(fp, "#include \"IOStream.h\"\n");
    322     fprintf(fp, "#include \"%s_%s_context.h\"\n\n\n", m_basename.c_str(), sideString(CLIENT_SIDE));
    323 
    324     for (size_t i = 0; i < m_encoderHeaders.size(); i++) {
    325         fprintf(fp, "#include %s\n", m_encoderHeaders[i].c_str());
    326     }
    327     fprintf(fp, "\n");
    328 
    329     fprintf(fp, "struct %s : public %s_%s_context_t {\n\n",
    330             classname.c_str(), m_basename.c_str(), sideString(CLIENT_SIDE));
    331     fprintf(fp, "\tIOStream *m_stream;\n\n");
    332 
    333     fprintf(fp, "\t%s(IOStream *stream);\n\n", classname.c_str());
    334     fprintf(fp, "\n};\n\n");
    335 
    336     fprintf(fp,"extern \"C\" {\n");
    337 
    338     for (size_t i = 0; i < size(); i++) {
    339         fprintf(fp, "\t");
    340         at(i).print(fp, false, "_enc", /* classname + "::" */"", "void *self");
    341         fprintf(fp, ";\n");
    342     }
    343     fprintf(fp, "};\n");
    344     fprintf(fp, "#endif");
    345 
    346     fclose(fp);
    347     return 0;
    348 }
    349 
    350 // Format the byte length expression for a given variable into a user-provided buffer
    351 // If the variable type is not a pointer, this is simply its size as a decimal constant
    352 // If the variable is a pointer, this will be an expression provided by the .attrib file
    353 // through the 'len' attribute.
    354 //
    355 // Returns 1 if the variable is a pointer, 0 otherwise
    356 //
    357 static int getVarEncodingSizeExpression(Var&  var, EntryPoint* e, char* buff, size_t bufflen)
    358 {
    359     int ret = 0;
    360     if (!var.isPointer()) {
    361         snprintf(buff, bufflen, "%u", (unsigned int) var.type()->bytes());
    362     } else {
    363         ret = 1;
    364         const char* lenExpr = var.lenExpression().c_str();
    365         const char* varname = var.name().c_str();
    366         if (e != NULL && lenExpr[0] == '\0') {
    367             fprintf(stderr, "%s: data len is undefined for '%s'\n",
    368                     e->name().c_str(), varname);
    369         }
    370         if (var.nullAllowed()) {
    371             snprintf(buff, bufflen, "((%s != NULL) ? %s : 0)", varname, lenExpr);
    372         } else {
    373             snprintf(buff, bufflen, "%s", lenExpr);
    374         }
    375     }
    376     return ret;
    377 }
    378 
    379 static int writeVarEncodingSize(Var& var, FILE* fp)
    380 {
    381     int ret = 0;
    382     if (!var.isPointer()) {
    383         fprintf(fp, "%u", (unsigned int) var.type()->bytes());
    384     } else {
    385         ret = 1;
    386         fprintf(fp, "__size_%s", var.name().c_str());
    387     }
    388     return ret;
    389 }
    390 
    391 
    392 
    393 static void writeVarEncodingExpression(Var& var, FILE* fp)
    394 {
    395     const char* varname = var.name().c_str();
    396 
    397     if (var.isPointer()) {
    398         // encode a pointer header
    399         fprintf(fp, "\t*(unsigned int *)(ptr) = __size_%s; ptr += 4;\n", varname);
    400 
    401         Var::PointerDir dir = var.pointerDir();
    402         if (dir == Var::POINTER_INOUT || dir == Var::POINTER_IN) {
    403             if (var.nullAllowed()) {
    404                 fprintf(fp, "\tif (%s != NULL) ", varname);
    405             } else {
    406                 fprintf(fp, "\t");
    407             }
    408 
    409             if (var.packExpression().size() != 0) {
    410                 fprintf(fp, "%s;", var.packExpression().c_str());
    411             } else {
    412                 fprintf(fp, "memcpy(ptr, %s, __size_%s);",
    413                         varname, varname);
    414             }
    415 
    416             fprintf(fp, "ptr += __size_%s;\n", varname);
    417         }
    418     } else {
    419         // encode a non pointer variable
    420         if (!var.isVoid()) {
    421             fprintf(fp, "\t\tmemcpy(ptr, &%s, %u); ptr += %u;\n",
    422                     varname,
    423                     (unsigned) var.type()->bytes(),
    424                     (unsigned) var.type()->bytes());
    425         }
    426     }
    427 }
    428 
    429 #if WITH_LARGE_SUPPORT
    430 static void writeVarLargeEncodingExpression(Var& var, FILE* fp)
    431 {
    432     const char* varname = var.name().c_str();
    433 
    434     fprintf(fp, "\tstream->writeFully(&__size_%s,4);\n", varname);
    435     if (var.nullAllowed()) {
    436         fprintf(fp, "\tif (%s != NULL) ", varname);
    437     } else {
    438         fprintf(fp, "\t");
    439     }
    440     if (var.writeExpression() != "") {
    441         fprintf(fp, "%s", var.writeExpression().c_str());
    442     } else {
    443         fprintf(fp, "stream->writeFully(%s, __size_%s)", varname, varname);
    444     }
    445     fprintf(fp, ";\n");
    446 }
    447 #endif /* WITH_LARGE_SUPPORT */
    448 
    449 int ApiGen::genEncoderImpl(const std::string &filename)
    450 {
    451     FILE *fp = fopen(filename.c_str(), "wt");
    452     if (fp == NULL) {
    453         perror(filename.c_str());
    454         return -1;
    455     }
    456 
    457     printHeader(fp);
    458     fprintf(fp, "\n\n#include <string.h>\n");
    459     fprintf(fp, "#include \"%s_opcodes.h\"\n\n", m_basename.c_str());
    460     fprintf(fp, "#include \"%s_enc.h\"\n\n\n", m_basename.c_str());
    461     fprintf(fp, "#include <stdio.h>\n");
    462     std::string classname = m_basename + "_encoder_context_t";
    463     size_t n = size();
    464 
    465     // unsupport printout
    466     fprintf(fp,
    467             "static void enc_unsupported()\n{\n\tALOGE(\"Function is unsupported\\n\");\n}\n\n");
    468 
    469     // entry points;
    470     for (size_t i = 0; i < n; i++) {
    471         EntryPoint *e = &at(i);
    472 
    473         if (e->unsupported()) continue;
    474 
    475 
    476         e->print(fp, true, "_enc", /* classname + "::" */"", "void *self");
    477         fprintf(fp, "{\n");
    478 
    479 //      fprintf(fp, "\n\tDBG(\">>>> %s\\n\");\n", e->name().c_str());
    480         fprintf(fp, "\n\t%s *ctx = (%s *)self;\n",
    481                 classname.c_str(),
    482                 classname.c_str());
    483         fprintf(fp, "\tIOStream *stream = ctx->m_stream;\n\n");
    484         VarsArray & evars = e->vars();
    485         size_t  maxvars = evars.size();
    486         size_t  j;
    487 
    488         char    buff[256];
    489 
    490         // Define the __size_XXX variables that contain the size of data
    491         // associated with pointers.
    492         for (j = 0; j < maxvars; j++) {
    493             Var& var = evars[j];
    494 
    495             if (!var.isPointer())
    496                 continue;
    497 
    498             const char* varname = var.name().c_str();
    499             fprintf(fp, "\tconst unsigned int __size_%s = ", varname);
    500 
    501             getVarEncodingSizeExpression(var, e, buff, sizeof(buff));
    502             fprintf(fp, "%s;\n", buff);
    503         }
    504 
    505 #if WITH_LARGE_SUPPORT
    506         // We need to take care of 'isLarge' variable in a special way
    507         // Anything before an isLarge variable can be packed into a single
    508         // buffer, which is then commited. Each isLarge variable is a pointer
    509         // to data that can be written to directly through the pipe, which
    510         // will be instant when using a QEMU pipe
    511 
    512         size_t  nvars   = 0;
    513         size_t  npointers = 0;
    514 
    515         // First, compute the total size, 8 bytes for the opcode + payload size
    516         fprintf(fp, "\t unsigned char *ptr;\n");
    517         fprintf(fp, "\t const size_t packetSize = 8");
    518 
    519         for (j = 0; j < maxvars; j++) {
    520             fprintf(fp, " + ");
    521             npointers += writeVarEncodingSize(evars[j], fp);
    522         }
    523         if (npointers > 0) {
    524             fprintf(fp, " + %zu*4", npointers);
    525         }
    526         fprintf(fp, ";\n");
    527 
    528         // We need to divide the packet into fragments. Each fragment contains
    529         // either copied arguments to a temporary buffer, or direct writes for
    530         // large variables.
    531         //
    532         // The first fragment must also contain the opcode+payload_size
    533         //
    534         nvars = 0;
    535         while (nvars < maxvars || maxvars == 0) {
    536 
    537             // Skip over non-large fields
    538             for (j = nvars; j < maxvars; j++) {
    539                 if (evars[j].isLarge())
    540                     break;
    541             }
    542 
    543             // Write a fragment if needed.
    544             if (nvars == 0 || j > nvars) {
    545                 const char* plus = "";
    546 
    547                 if (nvars == 0 && j == maxvars) {
    548                     // Simple shortcut for the common case where we don't have large variables;
    549                     fprintf(fp, "\tptr = stream->alloc(packetSize);\n");
    550 
    551                 } else {
    552                     // allocate buffer from the stream until the first large variable
    553                     fprintf(fp, "\tptr = stream->alloc(");
    554                     plus = "";
    555 
    556                     if (nvars == 0) {
    557                         fprintf(fp,"8"); plus = " + ";
    558                     }
    559                     if (j > nvars) {
    560                         npointers = 0;
    561                         for (j = nvars; j < maxvars && !evars[j].isLarge(); j++) {
    562                             fprintf(fp, "%s", plus); plus = " + ";
    563                             npointers += writeVarEncodingSize(evars[j], fp);
    564                         }
    565                         if (npointers > 0) {
    566                             fprintf(fp, "%s%zu*4", plus, npointers); plus = " + ";
    567                         }
    568                     }
    569                     fprintf(fp,");\n");
    570                 }
    571 
    572                 // encode packet header if needed.
    573                 if (nvars == 0) {
    574                     fprintf(fp, "\tint tmp = OP_%s;memcpy(ptr, &tmp, 4); ptr += 4;\n",  e->name().c_str());
    575                     fprintf(fp, "\tmemcpy(ptr, &packetSize, 4);  ptr += 4;\n\n");
    576                 }
    577 
    578                 if (maxvars == 0)
    579                     break;
    580 
    581                 // encode non-large fields in this fragment
    582                 for (j = nvars; j < maxvars && !evars[j].isLarge(); j++) {
    583                     writeVarEncodingExpression(evars[j],fp);
    584                 }
    585 
    586                 // Ensure the fragment is commited if it is followed by a large variable
    587                 if (j < maxvars) {
    588                     fprintf(fp, "\tstream->flush();\n");
    589                 }
    590             }
    591 
    592             // If we have one or more large variables, write them directly.
    593             // As size + data
    594             for ( ; j < maxvars && evars[j].isLarge(); j++) {
    595                 writeVarLargeEncodingExpression(evars[j], fp);
    596             }
    597 
    598             nvars = j;
    599         }
    600 
    601 #else /* !WITH_LARGE_SUPPORT */
    602         size_t nvars = evars.size();
    603         size_t npointers = 0;
    604         fprintf(fp, "\t const size_t packetSize = 8");
    605         for (size_t j = 0; j < nvars; j++) {
    606             npointers += getVarEncodingSizeExpression(evars[j],e,buff,sizeof(buff));
    607             fprintf(fp, " + %s", buff);
    608         }
    609         fprintf(fp, " + %u * 4;\n", (unsigned int) npointers);
    610 
    611         // allocate buffer from the stream;
    612         fprintf(fp, "\t unsigned char *ptr = stream->alloc(packetSize);\n\n");
    613 
    614         // encode into the stream;
    615         fprintf(fp, "\tint tmp = OP_%s; memcpy(ptr, &tmp, 4); ptr += 4;\n",  e->name().c_str());
    616         fprintf(fp, "\tmemcpy(ptr, &packetSize, 4);  ptr += 4;\n\n");
    617 
    618         // out variables
    619         for (size_t j = 0; j < nvars; j++) {
    620             writeVarEncodingExpression(evars[j], fp);
    621         }
    622 #endif /* !WITH_LARGE_SUPPORT */
    623 
    624         // in variables;
    625         for (size_t j = 0; j < nvars; j++) {
    626             if (evars[j].isPointer()) {
    627                 Var::PointerDir dir = evars[j].pointerDir();
    628                 if (dir == Var::POINTER_INOUT || dir == Var::POINTER_OUT) {
    629                     const char* varname = evars[j].name().c_str();
    630                     if (evars[j].nullAllowed()) {
    631                         fprintf(fp, "\tif (%s != NULL) ",varname);
    632                     } else {
    633                         fprintf(fp, "\t");
    634                     }
    635                     fprintf(fp, "stream->readback(%s, __size_%s);\n",
    636                             varname, varname);
    637                 }
    638             }
    639         }
    640 //XXX       fprintf(fp, "\n\tDBG(\"<<<< %s\\n\");\n", e->name().c_str());
    641         // todo - return value for pointers
    642         if (e->retval().isPointer()) {
    643             fprintf(stderr, "WARNING: %s : return value of pointer is unsupported\n",
    644                     e->name().c_str());
    645             fprintf(fp, "\t return NULL;\n");
    646         } else if (e->retval().type()->name() != "void") {
    647             fprintf(fp, "\n\t%s retval;\n", e->retval().type()->name().c_str());
    648             fprintf(fp, "\tstream->readback(&retval, %u);\n",(unsigned) e->retval().type()->bytes());
    649             fprintf(fp, "\treturn retval;\n");
    650         }
    651         fprintf(fp, "}\n\n");
    652     }
    653 
    654     // constructor
    655     fprintf(fp, "%s::%s(IOStream *stream)\n{\n", classname.c_str(), classname.c_str());
    656     fprintf(fp, "\tm_stream = stream;\n\n");
    657 
    658     for (size_t i = 0; i < n; i++) {
    659         EntryPoint *e = &at(i);
    660         if (e->unsupported()) {
    661             fprintf(fp, "\tset_%s((%s_%s_proc_t)(enc_unsupported));\n", e->name().c_str(), e->name().c_str(), sideString(CLIENT_SIDE));
    662         } else {
    663             fprintf(fp, "\tset_%s(%s_enc);\n", e->name().c_str(), e->name().c_str());
    664         }
    665         /**
    666            if (e->unsupsported()) {
    667            fprintf(fp, "\tmemcpy((void *)(&%s), (const void *)(&enc_unsupported), sizeof(%s));\n",
    668            e->name().c_str(),
    669            e->name().c_str());
    670            } else {
    671            fprintf(fp, "\t%s = %s_enc;\n", e->name().c_str(), e->name().c_str());
    672            }
    673         **/
    674     }
    675     fprintf(fp, "}\n\n");
    676 
    677     fclose(fp);
    678     return 0;
    679 }
    680 
    681 
    682 int ApiGen::genDecoderHeader(const std::string &filename)
    683 {
    684     FILE *fp = fopen(filename.c_str(), "wt");
    685     if (fp == NULL) {
    686         perror(filename.c_str());
    687         return -1;
    688     }
    689 
    690     printHeader(fp);
    691     std::string classname = m_basename + "_decoder_context_t";
    692 
    693     fprintf(fp, "\n#ifndef GUARD_%s\n", classname.c_str());
    694     fprintf(fp, "#define GUARD_%s\n\n", classname.c_str());
    695 
    696     fprintf(fp, "#include \"IOStream.h\" \n");
    697     fprintf(fp, "#include \"%s_%s_context.h\"\n\n\n", m_basename.c_str(), sideString(SERVER_SIDE));
    698 
    699     for (size_t i = 0; i < m_decoderHeaders.size(); i++) {
    700         fprintf(fp, "#include %s\n", m_decoderHeaders[i].c_str());
    701     }
    702     fprintf(fp, "\n");
    703 
    704     fprintf(fp, "struct %s : public %s_%s_context_t {\n\n",
    705             classname.c_str(), m_basename.c_str(), sideString(SERVER_SIDE));
    706     fprintf(fp, "\tsize_t decode(void *buf, size_t bufsize, IOStream *stream);\n");
    707     fprintf(fp, "\n};\n\n");
    708     fprintf(fp, "#endif\n");
    709 
    710     fclose(fp);
    711     return 0;
    712 }
    713 
    714 int ApiGen::genContextImpl(const std::string &filename, SideType side)
    715 {
    716     FILE *fp = fopen(filename.c_str(), "wt");
    717     if (fp == NULL) {
    718         perror(filename.c_str());
    719         return -1;
    720     }
    721     printHeader(fp);
    722 
    723     std::string classname = m_basename + "_" + sideString(side) + "_context_t";
    724     size_t n = size();
    725     fprintf(fp, "\n\n#include <string.h>\n");
    726     fprintf(fp, "#include \"%s_%s_context.h\"\n\n\n", m_basename.c_str(), sideString(side));
    727     fprintf(fp, "#include <stdio.h>\n\n");
    728 
    729     // init function;
    730     fprintf(fp, "int %s::initDispatchByName(void *(*getProc)(const char *, void *userData), void *userData)\n{\n", classname.c_str());
    731     fprintf(fp, "\tvoid *ptr;\n\n");
    732     for (size_t i = 0; i < n; i++) {
    733         EntryPoint *e = &at(i);
    734         fprintf(fp, "\tptr = getProc(\"%s\", userData); set_%s((%s_%s_proc_t)ptr);\n",
    735                 e->name().c_str(),
    736                 e->name().c_str(),
    737                 e->name().c_str(),
    738                 sideString(side));
    739 
    740     }
    741     fprintf(fp, "\treturn 0;\n");
    742     fprintf(fp, "}\n\n");
    743     fclose(fp);
    744     return 0;
    745 }
    746 
    747 int ApiGen::genDecoderImpl(const std::string &filename)
    748 {
    749     FILE *fp = fopen(filename.c_str(), "wt");
    750     if (fp == NULL) {
    751         perror(filename.c_str());
    752         return -1;
    753     }
    754 
    755     printHeader(fp);
    756 
    757     std::string classname = m_basename + "_decoder_context_t";
    758 
    759     size_t n = size();
    760 
    761     fprintf(fp, "\n\n#include <string.h>\n");
    762     fprintf(fp, "#include \"%s_opcodes.h\"\n\n", m_basename.c_str());
    763     fprintf(fp, "#include \"%s_dec.h\"\n\n\n", m_basename.c_str());
    764     fprintf(fp, "#include <stdio.h>\n\n");
    765     fprintf(fp, "typedef unsigned int tsize_t; // Target \"size_t\", which is 32-bit for now. It may or may not be the same as host's size_t when emugen is compiled.\n\n");
    766 
    767     // decoder switch;
    768     fprintf(fp, "size_t %s::decode(void *buf, size_t len, IOStream *stream)\n{\n", classname.c_str());
    769     fprintf(fp,
    770             "                           \n\
    771 \tsize_t pos = 0;\n\
    772 \tif (len < 8) return pos; \n\
    773 \tunsigned char *ptr = (unsigned char *)buf;\n\
    774 \tbool unknownOpcode = false;  \n\
    775 #ifdef CHECK_GL_ERROR \n\
    776 \tchar lastCall[256] = {0}; \n\
    777 #endif \n\
    778 \twhile ((len - pos >= 8) && !unknownOpcode) {   \n\
    779 \t\tint opcode = *(int *)ptr;   \n\
    780 \t\tunsigned int packetLen = *(int *)(ptr + 4);\n\
    781 \t\tif (len - pos < packetLen)  return pos; \n\
    782 \t\tswitch(opcode) {\n");
    783 
    784     for (size_t f = 0; f < n; f++) {
    785         enum Pass_t { PASS_TmpBuffAlloc = 0, PASS_MemAlloc, PASS_DebugPrint, PASS_FunctionCall, PASS_Epilog, PASS_LAST };
    786         EntryPoint *e = &at(f);
    787 
    788         // construct a printout string;
    789         std::string printString = "";
    790         for (size_t i = 0; i < e->vars().size(); i++) {
    791             Var *v = &e->vars()[i];
    792             if (!v->isVoid())  printString += (v->isPointer() ? "%p(%u)" : v->type()->printFormat()) + " ";
    793         }
    794         printString += "";
    795         // TODO - add for return value;
    796 
    797         fprintf(fp, "\t\t\tcase OP_%s:\n", e->name().c_str());
    798         fprintf(fp, "\t\t\t{\n");
    799 
    800         bool totalTmpBuffExist = false;
    801         std::string totalTmpBuffOffset = "0";
    802         std::string *tmpBufOffset = new std::string[e->vars().size()];
    803 
    804         // construct retval type string
    805         std::string retvalType;
    806         if (!e->retval().isVoid()) {
    807             retvalType = e->retval().type()->name();
    808         }
    809 
    810         for (int pass = PASS_TmpBuffAlloc; pass < PASS_LAST; pass++) {
    811             if (pass == PASS_FunctionCall && !e->retval().isVoid() && !e->retval().isPointer()) {
    812                 fprintf(fp, "\t\t\t*(%s *)(&tmpBuf[%s]) = ", retvalType.c_str(),
    813                         totalTmpBuffOffset.c_str());
    814             }
    815 
    816 
    817             if (pass == PASS_FunctionCall) {
    818                 fprintf(fp, "\t\t\tthis->%s(", e->name().c_str());
    819                 if (e->customDecoder()) {
    820                     fprintf(fp, "this"); // add a context to the call
    821                 }
    822             } else if (pass == PASS_DebugPrint) {
    823                 fprintf(fp, "#ifdef DEBUG_PRINTOUT\n");
    824                 fprintf(fp, "\t\t\tfprintf(stderr,\"%s(%%p): %s(%s)\\n\", stream",
    825                         m_basename.c_str(), e->name().c_str(), printString.c_str());
    826                 if (e->vars().size() > 0 && !e->vars()[0].isVoid()) fprintf(fp, ",");
    827             }
    828 
    829             std::string varoffset = "8"; // skip the header
    830             VarsArray & evars = e->vars();
    831             // allocate memory for out pointers;
    832             for (size_t j = 0; j < evars.size(); j++) {
    833                 Var *v = & evars[j];
    834                 if (!v->isVoid()) {
    835                     if ((pass == PASS_FunctionCall) && (j != 0 || e->customDecoder())) fprintf(fp, ", ");
    836                     if (pass == PASS_DebugPrint && j != 0) fprintf(fp, ", ");
    837 
    838                     if (!v->isPointer()) {
    839                         if (pass == PASS_FunctionCall || pass == PASS_DebugPrint) {
    840                             fprintf(fp, "*(%s *)(ptr + %s)", v->type()->name().c_str(), varoffset.c_str());
    841                         }
    842                         varoffset += " + " + toString(v->type()->bytes());
    843                     } else {
    844                         if (v->pointerDir() == Var::POINTER_IN || v->pointerDir() == Var::POINTER_INOUT) {
    845                             if (pass == PASS_MemAlloc && v->pointerDir() == Var::POINTER_INOUT) {
    846                                 fprintf(fp, "\t\t\tsize_t tmpPtr%uSize = (size_t)*(unsigned int *)(ptr + %s);\n",
    847                                         (unsigned) j, varoffset.c_str());
    848                                 fprintf(fp, "unsigned char *tmpPtr%u = (ptr + %s + 4);\n",
    849                                         (unsigned) j, varoffset.c_str());
    850                             }
    851                             if (pass == PASS_FunctionCall) {
    852                                 if (v->nullAllowed()) {
    853                                     fprintf(fp, "*((unsigned int *)(ptr + %s)) == 0 ? NULL : (%s)(ptr + %s + 4)",
    854                                             varoffset.c_str(), v->type()->name().c_str(), varoffset.c_str());
    855                                 } else {
    856                                     fprintf(fp, "(%s)(ptr + %s + 4)",
    857                                             v->type()->name().c_str(), varoffset.c_str());
    858                                 }
    859                             } else if (pass == PASS_DebugPrint) {
    860                                 fprintf(fp, "(%s)(ptr + %s + 4), *(unsigned int *)(ptr + %s)",
    861                                         v->type()->name().c_str(), varoffset.c_str(),
    862                                         varoffset.c_str());
    863                             }
    864                             varoffset += " + 4 + *(tsize_t *)(ptr +" + varoffset + ")";
    865                         } else { // out pointer;
    866                             if (pass == PASS_TmpBuffAlloc) {
    867                                 fprintf(fp, "\t\t\tsize_t tmpPtr%uSize = (size_t)*(unsigned int *)(ptr + %s);\n",
    868                                         (unsigned) j, varoffset.c_str());
    869                                 if (!totalTmpBuffExist) {
    870                                     fprintf(fp, "\t\t\tsize_t totalTmpSize = tmpPtr%uSize;\n", (unsigned)j);
    871                                 } else {
    872                                     fprintf(fp, "\t\t\ttotalTmpSize += tmpPtr%uSize;\n", (unsigned)j);
    873                                 }
    874                                 tmpBufOffset[j] = totalTmpBuffOffset;
    875                                 char tmpPtrName[16];
    876                                 sprintf(tmpPtrName," + tmpPtr%uSize", (unsigned)j);
    877                                 totalTmpBuffOffset += std::string(tmpPtrName);
    878                                 totalTmpBuffExist = true;
    879                             } else if (pass == PASS_MemAlloc) {
    880                                 fprintf(fp, "\t\t\tunsigned char *tmpPtr%u = &tmpBuf[%s];\n",
    881                                         (unsigned)j, tmpBufOffset[j].c_str());
    882                                 fprintf(fp, "\t\t\tmemset(tmpPtr%u, 0, %s);\n",
    883                                         (unsigned)j,
    884                                         toString(v->type()->bytes()).c_str());
    885                             } else if (pass == PASS_FunctionCall) {
    886                                 if (v->nullAllowed()) {
    887                                     fprintf(fp, "tmpPtr%uSize == 0 ? NULL : (%s)(tmpPtr%u)",
    888                                             (unsigned) j, v->type()->name().c_str(), (unsigned) j);
    889                                 } else {
    890                                     fprintf(fp, "(%s)(tmpPtr%u)", v->type()->name().c_str(), (unsigned) j);
    891                                 }
    892                             } else if (pass == PASS_DebugPrint) {
    893                                 fprintf(fp, "(%s)(tmpPtr%u), *(unsigned int *)(ptr + %s)",
    894                                         v->type()->name().c_str(), (unsigned) j,
    895                                         varoffset.c_str());
    896                             }
    897                             varoffset += " + 4";
    898                         }
    899                     }
    900                 }
    901             }
    902 
    903             if (pass == PASS_FunctionCall || pass == PASS_DebugPrint) fprintf(fp, ");\n");
    904             if (pass == PASS_DebugPrint) fprintf(fp, "#endif\n");
    905 
    906             if (pass == PASS_TmpBuffAlloc) {
    907                 if (!e->retval().isVoid() && !e->retval().isPointer()) {
    908                     if (!totalTmpBuffExist)
    909                         fprintf(fp, "\t\t\tsize_t totalTmpSize = sizeof(%s);\n", retvalType.c_str());
    910                     else
    911                         fprintf(fp, "\t\t\ttotalTmpSize += sizeof(%s);\n", retvalType.c_str());
    912 
    913                     totalTmpBuffExist = true;
    914                 }
    915                 if (totalTmpBuffExist) {
    916                     fprintf(fp, "\t\t\tunsigned char *tmpBuf = stream->alloc(totalTmpSize);\n");
    917                 }
    918             }
    919 
    920             if (pass == PASS_Epilog) {
    921                 // send back out pointers data as well as retval
    922                 if (totalTmpBuffExist) {
    923                     fprintf(fp, "\t\t\tstream->flush();\n");
    924                 }
    925 
    926                 fprintf(fp, "\t\t\tpos += *(int *)(ptr + 4);\n");
    927                 fprintf(fp, "\t\t\tptr += *(int *)(ptr + 4);\n");
    928             }
    929 
    930         } // pass;
    931         fprintf(fp, "\t\t\t}\n");
    932         fprintf(fp, "#ifdef CHECK_GL_ERROR\n");
    933         fprintf(fp, "\t\t\tsprintf(lastCall, \"%s\");\n", e->name().c_str());
    934         fprintf(fp, "#endif\n");
    935         fprintf(fp, "\t\t\tbreak;\n");
    936 
    937         delete [] tmpBufOffset;
    938     }
    939     fprintf(fp, "\t\t\tdefault:\n");
    940     fprintf(fp, "\t\t\t\tunknownOpcode = true;\n");
    941     fprintf(fp, "\t\t} //switch\n");
    942     if (strstr(m_basename.c_str(), "gl")) {
    943         fprintf(fp, "#ifdef CHECK_GL_ERROR\n");
    944         fprintf(fp, "\tint err = lastCall[0] ? this->glGetError() : GL_NO_ERROR;\n");
    945         fprintf(fp, "\tif (err) fprintf(stderr, \"%s Error: 0x%%X in %%s\\n\", err, lastCall);\n", m_basename.c_str());
    946         fprintf(fp, "#endif\n");
    947     }
    948     fprintf(fp, "\t} // while\n");
    949     fprintf(fp, "\treturn pos;\n");
    950     fprintf(fp, "}\n");
    951 
    952     fclose(fp);
    953     return 0;
    954 }
    955 
    956 int ApiGen::readSpec(const std::string & filename)
    957 {
    958     FILE *specfp = fopen(filename.c_str(), "rt");
    959     if (specfp == NULL) {
    960         return -1;
    961     }
    962 
    963     char line[1000];
    964     unsigned int lc = 0;
    965     while (fgets(line, sizeof(line), specfp) != NULL) {
    966         lc++;
    967         EntryPoint ref;
    968         if (ref.parse(lc, std::string(line))) {
    969             push_back(ref);
    970             updateMaxEntryPointsParams(ref.vars().size());
    971         }
    972     }
    973     fclose(specfp);
    974     return 0;
    975 }
    976 
    977 int ApiGen::readAttributes(const std::string & attribFilename)
    978 {
    979     enum { ST_NAME, ST_ATT } state;
    980 
    981     FILE *fp = fopen(attribFilename.c_str(), "rt");
    982     if (fp == NULL) {
    983         perror(attribFilename.c_str());
    984         return -1;
    985     }
    986     char buf[1000];
    987 
    988     state = ST_NAME;
    989     EntryPoint *currentEntry = NULL;
    990     size_t lc = 0;
    991     bool globalAttributes = false;
    992     while (fgets(buf, sizeof(buf), fp) != NULL) {
    993         lc++;
    994         std::string line(buf);
    995         if (line.size() == 0) continue; // could that happen?
    996 
    997         if (line.at(0) == '#') continue; // comment
    998 
    999         size_t first = line.find_first_not_of(" \t\n");
   1000         if (state == ST_ATT && (first == std::string::npos || first == 0)) state = ST_NAME;
   1001 
   1002         line = trim(line);
   1003         if (line.size() == 0 || line.at(0) == '#') continue;
   1004 
   1005         switch(state) {
   1006         case ST_NAME:
   1007             if (line == "GLOBAL") {
   1008                 globalAttributes = true;
   1009             } else {
   1010                 globalAttributes = false;
   1011                 currentEntry = findEntryByName(line);
   1012                 if (currentEntry == NULL) {
   1013                     fprintf(stderr, "WARNING: %u: attribute of non existant entry point %s\n", (unsigned int)lc, line.c_str());
   1014                 }
   1015             }
   1016             state = ST_ATT;
   1017             break;
   1018         case ST_ATT:
   1019             if (globalAttributes) {
   1020                 setGlobalAttribute(line, lc);
   1021             } else  if (currentEntry != NULL) {
   1022                 currentEntry->setAttribute(line, lc);
   1023             }
   1024             break;
   1025         }
   1026     }
   1027     return 0;
   1028 }
   1029 
   1030 
   1031 int ApiGen::setGlobalAttribute(const std::string & line, size_t lc)
   1032 {
   1033     size_t pos = 0;
   1034     size_t last;
   1035     std::string token = getNextToken(line, pos, &last, WHITESPACE);
   1036     pos = last;
   1037 
   1038     if (token == "base_opcode") {
   1039         std::string str = getNextToken(line, pos, &last, WHITESPACE);
   1040         if (str.size() == 0) {
   1041             fprintf(stderr, "line %u: missing value for base_opcode\n", (unsigned) lc);
   1042         } else {
   1043             setBaseOpcode(atoi(str.c_str()));
   1044         }
   1045     } else  if (token == "encoder_headers") {
   1046         std::string str = getNextToken(line, pos, &last, WHITESPACE);
   1047         pos = last;
   1048         while (str.size() != 0) {
   1049             encoderHeaders().push_back(str);
   1050             str = getNextToken(line, pos, &last, WHITESPACE);
   1051             pos = last;
   1052         }
   1053     } else if (token == "client_context_headers") {
   1054         std::string str = getNextToken(line, pos, &last, WHITESPACE);
   1055         pos = last;
   1056         while (str.size() != 0) {
   1057             clientContextHeaders().push_back(str);
   1058             str = getNextToken(line, pos, &last, WHITESPACE);
   1059             pos = last;
   1060         }
   1061     } else if (token == "server_context_headers") {
   1062         std::string str = getNextToken(line, pos, &last, WHITESPACE);
   1063         pos = last;
   1064         while (str.size() != 0) {
   1065             serverContextHeaders().push_back(str);
   1066             str = getNextToken(line, pos, &last, WHITESPACE);
   1067             pos = last;
   1068         }
   1069     } else if (token == "decoder_headers") {
   1070         std::string str = getNextToken(line, pos, &last, WHITESPACE);
   1071         pos = last;
   1072         while (str.size() != 0) {
   1073             decoderHeaders().push_back(str);
   1074             str = getNextToken(line, pos, &last, WHITESPACE);
   1075             pos = last;
   1076         }
   1077     }
   1078     else {
   1079         fprintf(stderr, "WARNING: %u : unknown global attribute %s\n", (unsigned int)lc, line.c_str());
   1080     }
   1081 
   1082     return 0;
   1083 }
   1084 
   1085