Merge "Emugen : A tool to generate wire protocol code"

This commit is contained in:
David Turner
2011-03-16 07:55:55 -07:00
committed by Android Code Review
14 changed files with 2179 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
LOCAL_PATH:=$(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := debug
LOCAL_SRC_FILES := ApiGen.cpp EntryPoint.cpp main.cpp strUtils.cpp TypeFactory.cpp
LOCAL_MODULE := emugen
include $(BUILD_HOST_EXECUTABLE)

View File

@@ -0,0 +1,805 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ApiGen.h"
#include "EntryPoint.h"
#include <stdio.h>
#include <stdlib.h>
#include "strUtils.h"
#include <errno.h>
#include <sys/types.h>
EntryPoint * ApiGen::findEntryByName(const std::string & name)
{
EntryPoint * entry = NULL;
size_t n = this->size();
for (size_t i = 0; i < n; i++) {
if (at(i).name() == name) {
entry = &(at(i));
break;
}
}
return entry;
}
void ApiGen::printHeader(FILE *fp) const
{
fprintf(fp, "// Generated Code - DO NOT EDIT !!\n");
fprintf(fp, "// generated by 'emugen'\n");
}
int ApiGen::genProcTypes(const std::string &filename, SideType side)
{
FILE *fp = fopen(filename.c_str(), "wt");
if (fp == NULL) {
perror(filename.c_str());
return -1;
}
printHeader(fp);
fprintf(fp, "#ifndef __%s_%s_proc_t_h\n", m_basename.c_str(), sideString(side));
fprintf(fp, "#define __%s_%s_proc_t_h\n", m_basename.c_str(), sideString(side));
fprintf(fp, "\n\n");
fprintf(fp, "\n#include \"%s_types.h\"\n", m_basename.c_str());
for (size_t i = 0; i < size(); i++) {
EntryPoint *e = &at(i);
fprintf(fp, "typedef ");
e->retval().printType(fp);
fprintf(fp, " (* %s_%s_proc_t) (", e->name().c_str(), sideString(side));
if (side == CLIENT_SIDE) { fprintf(fp, "void * ctx"); }
if (e->customDecoder() && side == SERVER_SIDE) { fprintf(fp, "void *ctx"); }
VarsArray & evars = e->vars();
size_t n = evars.size();
for (size_t j = 0; j < n; j++) {
if (!evars[j].isVoid()) {
if (j != 0 || side == CLIENT_SIDE || (side == SERVER_SIDE && e->customDecoder())) fprintf(fp, ", ");
evars[j].printType(fp);
}
}
fprintf(fp, ");\n");
}
fprintf(fp, "\n\n#endif\n");
return 0;
}
int ApiGen::genContext(const std::string & filename, SideType side)
{
FILE *fp = fopen(filename.c_str(), "wt");
if (fp == NULL) {
perror(filename.c_str());
return -1;
}
printHeader(fp);
fprintf(fp, "#ifndef __%s_%s_context_t_h\n", m_basename.c_str(), sideString(side));
fprintf(fp, "#define __%s_%s_context_t_h\n", m_basename.c_str(), sideString(side));
// fprintf(fp, "\n#include \"%s_types.h\"\n", m_basename.c_str());
fprintf(fp, "\n#include \"%s_%s_proc.h\"\n", m_basename.c_str(), sideString(side));
StringVec & contextHeaders = side == CLIENT_SIDE ? m_clientContextHeaders : m_serverContextHeaders;
for (size_t i = 0; i < contextHeaders.size(); i++) {
fprintf(fp, "#include %s\n", contextHeaders[i].c_str());
}
fprintf(fp, "\n");
fprintf(fp, "\nstruct %s_%s_context_t {\n\n", m_basename.c_str(), sideString(side));
for (size_t i = 0; i < size(); i++) {
EntryPoint *e = &at(i);
fprintf(fp, "\t%s_%s_proc_t %s;\n", e->name().c_str(), sideString(side), e->name().c_str());
}
// accessors
fprintf(fp, "\t//Accessors \n");
for (size_t i = 0; i < size(); i++) {
EntryPoint *e = &at(i);
const char *n = e->name().c_str();
const char *s = sideString(side);
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);
}
// virtual destructor
fprintf(fp, "\t virtual ~%s_%s_context_t() {}\n", m_basename.c_str(), sideString(side));
// accessor
if (side == CLIENT_SIDE) {
fprintf(fp, "\n\ttypedef %s_%s_context_t *CONTEXT_ACCESSOR_TYPE(void);\n",
m_basename.c_str(), sideString(side));
fprintf(fp, "\tvoid setContextAccessor(CONTEXT_ACCESSOR_TYPE *f);\n");
}
fprintf(fp, "};\n");
fprintf(fp, "\n#endif\n");
fclose(fp);
return 0;
}
int ApiGen::genClientEntryPoints(const std::string & filename)
{
FILE *fp = fopen(filename.c_str(), "wt");
if (fp == NULL) {
perror(filename.c_str());
return errno;
}
printHeader(fp);
fprintf(fp, "#include <stdio.h>\n");
fprintf(fp, "#include <stdlib.h>\n");
fprintf(fp, "#include \"%s_%s_context.h\"\n", m_basename.c_str(), sideString(CLIENT_SIDE));
fprintf(fp, "\n");
fprintf(fp, "extern \"C\" {\n");
for (size_t i = 0; i < size(); i++) {
fprintf(fp, "\t"); at(i).print(fp, false); fprintf(fp, ";\n");
}
fprintf(fp, "};\n\n");
fprintf(fp, "static %s_%s_context_t::CONTEXT_ACCESSOR_TYPE *getCurrentContext = NULL;\n",
m_basename.c_str(), sideString(CLIENT_SIDE));
fprintf(fp,
"void %s_%s_context_t::setContextAccessor(CONTEXT_ACCESSOR_TYPE *f) { getCurrentContext = f; }\n\n",
m_basename.c_str(), sideString(CLIENT_SIDE));
for (size_t i = 0; i < size(); i++) {
EntryPoint *e = &at(i);
e->print(fp);
fprintf(fp, "{\n");
fprintf(fp, "\t %s_%s_context_t * ctx = getCurrentContext(); \n",
m_basename.c_str(), sideString(CLIENT_SIDE));
bool shouldReturn = !e->retval().isVoid();
fprintf(fp, "\t %sctx->%s(ctx",
shouldReturn ? "return " : "",
e->name().c_str());
size_t nvars = e->vars().size();
for (size_t j = 0; j < nvars; j++) {
if (!e->vars()[j].isVoid()) {
fprintf(fp, ", %s", e->vars()[j].name().c_str());
}
}
fprintf(fp, ");\n");
fprintf(fp, "}\n\n");
}
fclose(fp);
return 0;
}
int ApiGen::genOpcodes(const std::string &filename)
{
FILE *fp = fopen(filename.c_str(), "wt");
if (fp == NULL) {
perror(filename.c_str());
return errno;
}
printHeader(fp);
fprintf(fp, "#ifndef __GUARD_%s_opcodes_h_\n", m_basename.c_str());
fprintf(fp, "#define __GUARD_%s_opcodes_h_\n\n", m_basename.c_str());
for (size_t i = 0; i < size(); i++) {
fprintf(fp, "#define OP_%s \t\t\t\t\t%u\n", at(i).name().c_str(), (unsigned int)i + m_baseOpcode);
}
fprintf(fp, "#define OP_last \t\t\t\t\t%u\n", (unsigned int)size() + m_baseOpcode);
fprintf(fp,"\n\n#endif\n");
fclose(fp);
return 0;
}
int ApiGen::genAttributesTemplate(const std::string &filename )
{
FILE *fp = fopen(filename.c_str(), "wt");
if (fp == NULL) {
perror(filename.c_str());
return -1;
}
for (size_t i = 0; i < size(); i++) {
if (at(i).hasPointers()) {
fprintf(fp, "#");
at(i).print(fp);
fprintf(fp, "%s\n\n", at(i).name().c_str());
}
}
fclose(fp);
return 0;
}
int ApiGen::genEncoderHeader(const std::string &filename)
{
FILE *fp = fopen(filename.c_str(), "wt");
if (fp == NULL) {
perror(filename.c_str());
return -1;
}
printHeader(fp);
std::string classname = m_basename + "_encoder_context_t";
fprintf(fp, "\n#ifndef GUARD_%s\n", classname.c_str());
fprintf(fp, "#define GUARD_%s\n\n", classname.c_str());
fprintf(fp, "#include \"IOStream.h\"\n");
fprintf(fp, "#include \"%s_%s_context.h\"\n\n\n", m_basename.c_str(), sideString(CLIENT_SIDE));
for (size_t i = 0; i < m_encoderHeaders.size(); i++) {
fprintf(fp, "#include %s\n", m_encoderHeaders[i].c_str());
}
fprintf(fp, "\n");
fprintf(fp, "struct %s : public %s_%s_context_t {\n\n",
classname.c_str(), m_basename.c_str(), sideString(CLIENT_SIDE));
fprintf(fp, "\tIOStream *m_stream;\n\n");
fprintf(fp, "\t%s(IOStream *stream);\n\n", classname.c_str());
fprintf(fp, "\n};\n\n");
fprintf(fp,"extern \"C\" {\n");
for (size_t i = 0; i < size(); i++) {
fprintf(fp, "\t");
at(i).print(fp, false, "_enc", /* classname + "::" */"", "void *self");
fprintf(fp, ";\n");
}
fprintf(fp, "};\n");
fprintf(fp, "#endif");
fclose(fp);
return 0;
}
int ApiGen::genEncoderImpl(const std::string &filename)
{
FILE *fp = fopen(filename.c_str(), "wt");
if (fp == NULL) {
perror(filename.c_str());
return -1;
}
printHeader(fp);
fprintf(fp, "\n\n#include <string.h>\n");
fprintf(fp, "#include \"%s_opcodes.h\"\n\n", m_basename.c_str());
fprintf(fp, "#include \"%s_enc.h\"\n\n\n", m_basename.c_str());
fprintf(fp, "#include <stdio.h>\n");
std::string classname = m_basename + "_encoder_context_t";
size_t n = size();
// unsupport printout
fprintf(fp, "static void enc_unsupported()\n{\n\tfprintf(stderr, \"Function is unsupported\\n\");\n}\n\n");
// entry points;
for (size_t i = 0; i < n; i++) {
EntryPoint *e = &at(i);
if (e->unsupported()) continue;
e->print(fp, true, "_enc", /* classname + "::" */"", "void *self");
fprintf(fp, "{\n");
fprintf(fp, "\n\t%s *ctx = (%s *)self;\n\n",
classname.c_str(),
classname.c_str());
// size calculation ;
fprintf(fp, "\t size_t packetSize = ");
VarsArray & evars = e->vars();
size_t nvars = evars.size();
size_t npointers = 0;
for (size_t j = 0; j < nvars; j++) {
fprintf(fp, "%s ", j == 0 ? "" : " +");
if (evars[j].isPointer()) {
npointers++;
if (evars[j].lenExpression() == "") {
fprintf(stderr, "%s: data len is undefined for '%s'\n",
e->name().c_str(), evars[j].name().c_str());
}
if (evars[j].nullAllowed()) {
fprintf(fp, "(%s != NULL ? %s : 0)",
evars[j].name().c_str(),
evars[j].lenExpression().c_str());
} else {
if (evars[j].pointerDir() == Var::POINTER_IN ||
evars[j].pointerDir() == Var::POINTER_INOUT) {
fprintf(fp, "%s", evars[j].lenExpression().c_str());
} else {
fprintf(fp, "0");
}
}
} else {
fprintf(fp, "%u", (unsigned int) evars[j].type()->bytes());
}
}
fprintf(fp, " %s 8 + %u * 4;\n", nvars != 0 ? "+" : "", (unsigned int) npointers);
// allocate buffer from the stream;
fprintf(fp, "\t unsigned char *ptr = ctx->m_stream->alloc(packetSize);\n\n");
// encode into the stream;
fprintf(fp, "\t*(unsigned int *)(ptr) = OP_%s; ptr += 4;\n", e->name().c_str());
fprintf(fp, "\t*(unsigned int *)(ptr) = (unsigned int) packetSize; ptr += 4;\n\n");
// out variables
for (size_t j = 0; j < nvars; j++) {
if (evars[j].isPointer()) {
// encode a pointer header
if (evars[j].nullAllowed()) {
fprintf(fp, "\t*(unsigned int *)(ptr) = (%s != NULL) ? %s : 0; ptr += 4; \n",
evars[j].name().c_str(), evars[j].lenExpression().c_str());
} else {
fprintf(fp, "\t*(unsigned int *)(ptr) = %s; ptr += 4; \n",
evars[j].lenExpression().c_str());
}
Var::PointerDir dir = evars[j].pointerDir();
if (dir == Var::POINTER_INOUT || dir == Var::POINTER_IN) {
if (evars[j].nullAllowed()) {
fprintf(fp, "\tif (%s != NULL) ", evars[j].name().c_str());
} else {
fprintf(fp, "\t");
}
if (evars[j].packExpression().size() != 0) {
fprintf(fp, "%s;", evars[j].packExpression().c_str());
} else {
fprintf(fp, "memcpy(ptr, %s, %s);",
evars[j].name().c_str(),
evars[j].lenExpression().c_str());
}
fprintf(fp, "ptr += %s;\n", evars[j].lenExpression().c_str());
}
} else {
// encode a non pointer variable
if (!evars[j].isVoid()) {
fprintf(fp, "\t*(%s *) (ptr) = %s; ptr += %u;\n",
evars[j].type()->name().c_str(), evars[j].name().c_str(),
(uint) evars[j].type()->bytes());
}
}
}
// in variables;
for (size_t j = 0; j < nvars; j++) {
if (evars[j].isPointer()) {
Var::PointerDir dir = evars[j].pointerDir();
if (dir == Var::POINTER_INOUT || dir == Var::POINTER_OUT) {
if (evars[j].nullAllowed()) {
fprintf(fp, "\tif (%s != NULL) ctx->m_stream->readback(%s, %s);\n",
evars[j].name().c_str(),
evars[j].name().c_str(),
evars[j].lenExpression().c_str());
} else {
fprintf(fp, "\tctx->m_stream->readback(%s, %s);\n",
evars[j].name().c_str(),
evars[j].lenExpression().c_str());
}
}
}
}
// todo - return value for pointers
if (e->retval().isPointer()) {
fprintf(stderr, "WARNING: %s : return value of pointer is unsupported\n",
e->name().c_str());
fprintf(fp, "\t return NULL;\n");
} else if (e->retval().type()->name() != "void") {
fprintf(fp, "\n\t%s retval;\n", e->retval().type()->name().c_str());
fprintf(fp, "\tctx->m_stream->readback(&retval, %u);\n",(uint) e->retval().type()->bytes());
fprintf(fp, "\treturn retval;\n");
}
fprintf(fp, "}\n\n");
}
// constructor
fprintf(fp, "%s::%s(IOStream *stream)\n{\n", classname.c_str(), classname.c_str());
fprintf(fp, "\tm_stream = stream;\n\n");
for (size_t i = 0; i < n; i++) {
EntryPoint *e = &at(i);
if (e->unsupported()) {
fprintf(fp, "\tset_%s((%s_%s_proc_t)(enc_unsupported));\n", e->name().c_str(), e->name().c_str(), sideString(CLIENT_SIDE));
} else {
fprintf(fp, "\tset_%s(%s_enc);\n", e->name().c_str(), e->name().c_str());
}
/**
if (e->unsupsported()) {
fprintf(fp, "\tmemcpy((void *)(&%s), (const void *)(&enc_unsupported), sizeof(%s));\n",
e->name().c_str(),
e->name().c_str());
} else {
fprintf(fp, "\t%s = %s_enc;\n", e->name().c_str(), e->name().c_str());
}
**/
}
fprintf(fp, "}\n\n");
fclose(fp);
return 0;
}
int ApiGen::genDecoderHeader(const std::string &filename)
{
FILE *fp = fopen(filename.c_str(), "wt");
if (fp == NULL) {
perror(filename.c_str());
return -1;
}
printHeader(fp);
std::string classname = m_basename + "_decoder_context_t";
fprintf(fp, "\n#ifndef GUARD_%s\n", classname.c_str());
fprintf(fp, "#define GUARD_%s\n\n", classname.c_str());
fprintf(fp, "#include \"IOStream.h\" \n");
fprintf(fp, "#include \"%s_%s_context.h\"\n\n\n", m_basename.c_str(), sideString(SERVER_SIDE));
for (size_t i = 0; i < m_decoderHeaders.size(); i++) {
fprintf(fp, "#include %s\n", m_decoderHeaders[i].c_str());
}
fprintf(fp, "\n");
fprintf(fp, "struct %s : public %s_%s_context_t {\n\n",
classname.c_str(), m_basename.c_str(), sideString(SERVER_SIDE));
fprintf(fp, "\tsize_t decode(void *buf, size_t bufsize, IOStream *stream);\n");
fprintf(fp, "\tint initDispatchByName( void *(*getProc)(const char *name, void *userData), void *userData);\n");
fprintf(fp, "\n};\n\n");
fprintf(fp, "#endif");
fclose(fp);
return 0;
}
int ApiGen::genDecoderImpl(const std::string &filename)
{
FILE *fp = fopen(filename.c_str(), "wt");
if (fp == NULL) {
perror(filename.c_str());
return -1;
}
printHeader(fp);
std::string classname = m_basename + "_decoder_context_t";
size_t n = size();
fprintf(fp, "\n\n#include <string.h>\n");
fprintf(fp, "#include \"%s_opcodes.h\"\n\n", m_basename.c_str());
fprintf(fp, "#include \"%s_dec.h\"\n\n\n", m_basename.c_str());
fprintf(fp, "#include <stdio.h>\n");
// init function;
fprintf(fp, "int %s::initDispatchByName(void *(*getProc)(const char *, void *userData), void *userData)\n{\n", classname.c_str());
fprintf(fp, "\tvoid *ptr;\n\n");
for (size_t i = 0; i < n; i++) {
EntryPoint *e = &at(i);
fprintf(fp, "\tptr = getProc(\"%s\", userData); set_%s((%s_%s_proc_t)ptr);\n",
e->name().c_str(),
e->name().c_str(),
e->name().c_str(),
sideString(SERVER_SIDE));
}
fprintf(fp, "\treturn 0;\n");
fprintf(fp, "}\n\n");
// decoder switch;
fprintf(fp, "size_t %s::decode(void *buf, size_t len, IOStream *stream)\n{\n", classname.c_str());
fprintf(fp,
" \n\
\tsize_t pos = 0;\n\
\tif (len < 8) return pos; \n\
\tunsigned char *ptr = (unsigned char *)buf;\n\
\tbool unknownOpcode = false; \n\
\twhile ((len - pos >= 8) && !unknownOpcode) { \n\
\t\tvoid *params[%u]; \n\
\t\tint opcode = *(int *)ptr; \n\
\t\tunsigned int packetLen = *(int *)(ptr + 4);\n\
\t\tif (len - pos < packetLen) return pos; \n\
\t\tswitch(opcode) {\n",
(uint) m_maxEntryPointsParams);
for (size_t f = 0; f < n; f++) {
enum Pass_t { PASS_TmpBuffAlloc = 0, PASS_MemAlloc, PASS_DebugPrint, PASS_FunctionCall, PASS_Epilog, PASS_LAST };
EntryPoint *e = &at(f);
// construct a printout string;
std::string printString = "";
for (size_t i = 0; i < e->vars().size(); i++) {
Var *v = &e->vars()[i];
if (!v->isVoid()) printString += (v->isPointer() ? "%p(%u)" : v->type()->printFormat()) + " ";
}
printString += "";
// TODO - add for return value;
fprintf(fp, "\t\t\tcase OP_%s:\n", e->name().c_str());
fprintf(fp, "\t\t\t{\n");
bool totalTmpBuffExist = false;
std::string totalTmpBuffOffset = "0";
std::string *tmpBufOffset = new std::string[e->vars().size()];
// construct retval type string
std::string retvalType;
if (!e->retval().isVoid()) {
retvalType = e->retval().type()->name();
if (e->retval().isPointer()) retvalType += "*";
}
for (int pass = PASS_TmpBuffAlloc; pass < PASS_LAST; pass++) {
if (pass == PASS_FunctionCall && !e->retval().isVoid() && !e->retval().isPointer()) {
fprintf(fp, "\t\t\t*(%s *)(&tmpBuf[%s]) = ", retvalType.c_str(),
totalTmpBuffOffset.c_str());
}
if (pass == PASS_FunctionCall) {
fprintf(fp, "\t\t\tthis->%s(", e->name().c_str());
if (e->customDecoder()) {
fprintf(fp, "this"); // add a context to the call
}
} else if (pass == PASS_DebugPrint) {
fprintf(fp, "#ifdef DEBUG_PRINTOUT\n");
fprintf(fp, "\t\t\tfprintf(stderr,\"%s(%s)\\n\"", e->name().c_str(), printString.c_str());
if (e->vars().size() > 0 && !e->vars()[0].isVoid()) fprintf(fp, ",");
}
std::string varoffset = "8"; // skip the header
VarsArray & evars = e->vars();
// allocate memory for out pointers;
for (size_t j = 0; j < evars.size(); j++) {
Var *v = & evars[j];
if (!v->isVoid()) {
if ((pass == PASS_FunctionCall) && (j != 0 || e->customDecoder())) fprintf(fp, ", ");
if (pass == PASS_DebugPrint && j != 0) fprintf(fp, ", ");
if (!v->isPointer()) {
if (pass == PASS_FunctionCall || pass == PASS_DebugPrint) {
fprintf(fp, "*(%s *)(ptr + %s)", v->type()->name().c_str(), varoffset.c_str());
}
varoffset += " + " + toString(v->type()->bytes());
} else {
if (v->pointerDir() == Var::POINTER_IN || v->pointerDir() == Var::POINTER_INOUT) {
if (pass == PASS_MemAlloc && v->pointerDir() == Var::POINTER_INOUT) {
fprintf(fp, "\t\t\tsize_t tmpPtr%uSize = (size_t)*(unsigned int *)(ptr + %s);\n",
(uint) j, varoffset.c_str());
fprintf(fp, "unsigned char *tmpPtr%u = (ptr + %s + 4);\n",
(uint) j, varoffset.c_str());
}
if (pass == PASS_FunctionCall) {
fprintf(fp, "(%s *)(ptr + %s + 4)",
v->type()->name().c_str(), varoffset.c_str());
} else if (pass == PASS_DebugPrint) {
fprintf(fp, "(%s *)(ptr + %s + 4), *(unsigned int *)(ptr + %s)",
v->type()->name().c_str(), varoffset.c_str(),
varoffset.c_str());
}
varoffset += " + 4 + *(size_t *)(ptr +" + varoffset + ")";
} else { // in pointer;
if (pass == PASS_TmpBuffAlloc) {
fprintf(fp, "\t\t\tsize_t tmpPtr%uSize = (size_t)*(unsigned int *)(ptr + %s);\n",
(uint) j, varoffset.c_str());
if (!totalTmpBuffExist)
fprintf(fp, "\t\t\tsize_t totalTmpSize = tmpPtr%uSize;\n", (uint)j);
else
fprintf(fp, "\t\t\ttotalTmpSize += tmpPtr%uSize;\n", (uint)j);
tmpBufOffset[j] = totalTmpBuffOffset;
char tmpPtrName[16];
sprintf(tmpPtrName,"tmpPtr%u", (uint)j);
totalTmpBuffOffset += std::string(tmpPtrName);
totalTmpBuffExist = true;
} else if (pass == PASS_MemAlloc) {
fprintf(fp, "\t\t\tunsigned char *tmpPtr%u = &tmpBuf[%s];\n",
(uint)j, tmpBufOffset[j].c_str());
} else if (pass == PASS_FunctionCall) {
fprintf(fp, "(%s *)(tmpPtr%u)", v->type()->name().c_str(), (uint) j);
} else if (pass == PASS_DebugPrint) {
fprintf(fp, "(%s *)(tmpPtr%u), *(unsigned int *)(ptr + %s)",
v->type()->name().c_str(), (uint) j,
varoffset.c_str());
}
varoffset += " + 4";
}
}
}
}
if (pass == PASS_FunctionCall || pass == PASS_DebugPrint) fprintf(fp, ");\n");
if (pass == PASS_DebugPrint) fprintf(fp, "#endif\n");
if (pass == PASS_TmpBuffAlloc) {
if (!e->retval().isVoid() && !e->retval().isPointer()) {
if (!totalTmpBuffExist)
fprintf(fp, "\t\t\tsize_t totalTmpSize = sizeof(%s);\n", retvalType.c_str());
else
fprintf(fp, "\t\t\ttotalTmpSize += sizeof(%s);\n", retvalType.c_str());
totalTmpBuffExist = true;
}
if (totalTmpBuffExist) {
fprintf(fp, "\t\t\tunsigned char *tmpBuf = stream->alloc(totalTmpSize);\n");
}
}
if (pass == PASS_Epilog) {
// send back out pointers data as well as retval
if (totalTmpBuffExist) {
fprintf(fp, "\t\t\tstream->flush();\n");
}
fprintf(fp, "\t\t\tpos += *(int *)(ptr + 4);\n");
fprintf(fp, "\t\t\tptr += *(int *)(ptr + 4);\n");
}
} // pass;
fprintf(fp, "\t\t\t}\n");
fprintf(fp, "\t\t\tbreak;\n");
delete [] tmpBufOffset;
}
fprintf(fp, "\t\t\tdefault:\n");
fprintf(fp, "\t\t\t\tunknownOpcode = true;\n");
fprintf(fp, "\t\t} //switch\n");
fprintf(fp, "\t} // while\n");
fprintf(fp, "\treturn pos;\n");
fprintf(fp, "}\n");
fclose(fp);
return 0;
}
int ApiGen::readSpec(const std::string & filename)
{
FILE *specfp = fopen(filename.c_str(), "rt");
if (specfp == NULL) {
return -1;
}
char line[1000];
unsigned int lc = 0;
while (fgets(line, sizeof(line), specfp) != NULL) {
lc++;
EntryPoint ref;
if (ref.parse(lc, std::string(line))) {
push_back(ref);
updateMaxEntryPointsParams(ref.vars().size());
}
}
fclose(specfp);
return 0;
}
int ApiGen::readAttributes(const std::string & attribFilename)
{
enum { ST_NAME, ST_ATT } state;
FILE *fp = fopen(attribFilename.c_str(), "rt");
if (fp == NULL) {
perror(attribFilename.c_str());
return -1;
}
char buf[1000];
state = ST_NAME;
EntryPoint *currentEntry = NULL;
size_t lc = 0;
bool globalAttributes = false;
while (fgets(buf, sizeof(buf), fp) != NULL) {
lc++;
std::string line(buf);
if (line.size() == 0) continue; // could that happen?
if (line.at(0) == '#') continue; // comment
size_t first = line.find_first_not_of(" \t\n");
if (state == ST_ATT && (first == std::string::npos || first == 0)) state = ST_NAME;
line = trim(line);
if (line.size() == 0 || line.at(0) == '#') continue;
switch(state) {
case ST_NAME:
if (line == "GLOBAL") {
globalAttributes = true;
} else {
globalAttributes = false;
currentEntry = findEntryByName(line);
if (currentEntry == NULL) {
fprintf(stderr, "WARNING: %u: attribute of non existant entry point %s\n", (unsigned int)lc, line.c_str());
}
}
state = ST_ATT;
break;
case ST_ATT:
if (globalAttributes) {
setGlobalAttribute(line, lc);
} else if (currentEntry != NULL) {
currentEntry->setAttribute(line, lc);
}
break;
}
}
return 0;
}
int ApiGen::setGlobalAttribute(const std::string & line, size_t lc)
{
size_t pos = 0;
size_t last;
std::string token = getNextToken(line, pos, &last, WHITESPACE);
pos = last;
if (token == "base_opcode") {
std::string str = getNextToken(line, pos, &last, WHITESPACE);
if (str.size() == 0) {
fprintf(stderr, "line %u: missing value for base_opcode\n", (uint) lc);
} else {
setBaseOpcode(atoi(str.c_str()));
}
} else if (token == "encoder_headers") {
std::string str = getNextToken(line, pos, &last, WHITESPACE);
pos = last;
while (str.size() != 0) {
encoderHeaders().push_back(str);
str = getNextToken(line, pos, &last, WHITESPACE);
pos = last;
}
} else if (token == "client_context_headers") {
std::string str = getNextToken(line, pos, &last, WHITESPACE);
pos = last;
while (str.size() != 0) {
clientContextHeaders().push_back(str);
str = getNextToken(line, pos, &last, WHITESPACE);
pos = last;
}
} else if (token == "server_context_headers") {
std::string str = getNextToken(line, pos, &last, WHITESPACE);
pos = last;
while (str.size() != 0) {
serverContextHeaders().push_back(str);
str = getNextToken(line, pos, &last, WHITESPACE);
pos = last;
}
} else if (token == "decoder_headers") {
std::string str = getNextToken(line, pos, &last, WHITESPACE);
pos = last;
while (str.size() != 0) {
decoderHeaders().push_back(str);
str = getNextToken(line, pos, &last, WHITESPACE);
pos = last;
}
}
else {
fprintf(stderr, "WARNING: %u : unknown global attribute %s\n", (unsigned int)lc, line.c_str());
}
return 0;
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __API_GEN_H_
#define __API_GEN_H_
#include <vector>
#include <string.h>
#include "EntryPoint.h"
class ApiGen : public std::vector<EntryPoint> {
public:
typedef std::vector<std::string> StringVec;
typedef enum { CLIENT_SIDE, SERVER_SIDE } SideType;
ApiGen(const std::string & basename) :
m_basename(basename),
m_maxEntryPointsParams(0),
m_baseOpcode(0)
{ }
virtual ~ApiGen() {}
int readSpec(const std::string & filename);
int readAttributes(const std::string & attribFilename);
size_t maxEntryPointsParams() { return m_maxEntryPointsParams; }
void updateMaxEntryPointsParams(size_t val) {
if (m_maxEntryPointsParams == 0 || val > m_maxEntryPointsParams) m_maxEntryPointsParams = val;
}
int baseOpcode() { return m_baseOpcode; }
void setBaseOpcode(int base) { m_baseOpcode = base; }
const char *sideString(SideType side) { return (side == CLIENT_SIDE) ? "client" : "server"; }
StringVec & clientContextHeaders() { return m_clientContextHeaders; }
StringVec & encoderHeaders() { return m_encoderHeaders; }
StringVec & serverContextHeaders() { return m_serverContextHeaders; }
StringVec & decoderHeaders() { return m_decoderHeaders; }
EntryPoint * findEntryByName(const std::string & name);
int genOpcodes(const std::string &filename);
int genAttributesTemplate(const std::string &filename);
int genProcTypes(const std::string &filename, SideType side);
int genContext(const std::string &filename, SideType side);
int genClientEntryPoints(const std::string &filename);
int genEncoderHeader(const std::string &filename);
int genEncoderImpl(const std::string &filename);
int genDecoderHeader(const std::string &filename);
int genDecoderImpl(const std::string &filename);
protected:
virtual void printHeader(FILE *fp) const;
std::string m_basename;
StringVec m_clientContextHeaders;
StringVec m_encoderHeaders;
StringVec m_serverContextHeaders;
StringVec m_decoderHeaders;
size_t m_maxEntryPointsParams; // record the maximum number of parameters in the entry points;
int m_baseOpcode;
int setGlobalAttribute(const std::string & line, size_t lc);
};
#endif

View File

@@ -0,0 +1,335 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
#include "EntryPoint.h"
#include <string>
#include "TypeFactory.h"
#include "strUtils.h"
#include <sstream>
EntryPoint::EntryPoint()
{
reset();
}
EntryPoint::~EntryPoint()
{
}
void EntryPoint::reset()
{
m_unsupported = false;
m_customDecoder = false;
m_vars.empty();
}
bool parseTypeField(const std::string & f, std::string *vartype, bool *pointer_type, std::string *varname)
{
size_t pos = 0, last;
bool done = false;
*vartype = "";
if (varname != NULL) *varname = "";
*pointer_type = false;
enum { ST_TYPE, ST_NAME, ST_END } state = ST_TYPE;
while(!done) {
std::string str = getNextToken(f, pos, &last, WHITESPACE);
if (str.size() == 0) break;
switch(state) {
case ST_TYPE:
if (str == "const") {
pos = last;
} else {
// must be a type name;
*vartype = str;
// do we have an astriks at the end of the name?
if (vartype->at(vartype->size() - 1) == '*') {
*pointer_type = true;
// remove the astriks
(*vartype)[vartype->size() - 1] = ' ';
*vartype = trim(*vartype);
}
state = ST_NAME;
pos = last;
}
break;
case ST_NAME:
if (str.size() == 0) {
done = true;
} else if (str == "*") {
*pointer_type = true;
// remove the leading astriks;
pos = last;
} else if (varname == NULL) {
done = true;
} else {
if (str[0] == '*') {
*pointer_type = true;
str[0] = ' ';
str = trim(str);
}
*varname = str;
done = true;
}
break;
case ST_END:
break;
}
}
return true;
}
// return true for valid line (need to get into the entry points list)
bool EntryPoint::parse(unsigned int lc, const std::string & str)
{
size_t pos, last;
std::string field;
reset();
std::string linestr = trim(str);
if (linestr.size() == 0) return false;
if (linestr.at(0) == '#') return false;
// skip PREFIX
field = getNextToken(linestr, 0, &last, "(");
pos = last + 1;
// return type
field = getNextToken(linestr, pos, &last, ",)");
std::string retTypeName;
bool pointer_type;
if (!parseTypeField(field, &retTypeName, &pointer_type, NULL)) {
fprintf(stderr, "line: %d: Parsing error in field <%s>\n", lc, field.c_str());
return false;
}
pos = last + 1;
const VarType *theType = TypeFactory::instance()->getVarTypeByName(retTypeName);
if (theType->name() == "UNKNOWN") {
fprintf(stderr, "UNKNOWN retval: %s\n", linestr.c_str());
}
m_retval.init(std::string(""), theType, pointer_type, std::string(""), Var::POINTER_OUT, std::string(""));
// function name
m_name = getNextToken(linestr, pos, &last, ",)");
pos = last + 1;
// parameters;
int nvars = 0;
while (pos < linestr.size() - 1) {
field = getNextToken(linestr, pos, &last, ",)");
std::string vartype, varname;
if (!parseTypeField(field, &vartype, &pointer_type, &varname)) {
fprintf(stderr, "line: %d: Parsing error in field <%s>\n", lc, field.c_str());
return false;
}
nvars++;
const VarType *v = TypeFactory::instance()->getVarTypeByName(vartype);
if (v->id() == 0) {
fprintf(stderr, "%d: Unknown type: %s\n", lc, vartype.c_str());
} else {
if (varname == "" &&
!(v->name() == "void" && !pointer_type)) {
std::ostringstream oss;
oss << "var" << nvars;
varname = oss.str();
}
m_vars.push_back(Var(varname, v, pointer_type, std::string(""), Var::POINTER_IN, ""));
}
pos = last + 1;
}
return true;
}
void EntryPoint::print(FILE *fp, bool newline,
const std::string & name_suffix,
const std::string & name_prefix,
const std::string & ctx_param ) const
{
fprintf(fp, "%s%s %s%s%s(",
m_retval.type()->name().c_str(),
m_retval.isPointer() ? "*" : "",
name_prefix.c_str(),
m_name.c_str(),
name_suffix.c_str());
if (ctx_param != "") fprintf(fp, "%s ", ctx_param.c_str());
for (size_t i = 0; i < m_vars.size(); i++) {
if (m_vars[i].isVoid()) continue;
if (i != 0 || ctx_param != "") fprintf(fp, ", ");
fprintf(fp, "%s %s%s", m_vars[i].type()->name().c_str(),
m_vars[i].isPointer() ? "*" : "",
m_vars[i].name().c_str());
}
fprintf(fp, ")%s", newline? "\n" : "");
}
Var * EntryPoint::var(const std::string & name)
{
Var *v = NULL;
for (size_t i = 0; i < m_vars.size(); i++) {
if (m_vars[i].name() == name) {
v = &m_vars[i];
break;
}
}
return v;
}
bool EntryPoint::hasPointers()
{
bool pointers = false;
if (m_retval.isPointer()) pointers = true;
if (!pointers) {
for (size_t i = 0; i < m_vars.size(); i++) {
if (m_vars[i].isPointer()) {
pointers = true;
break;
}
}
}
return pointers;
}
int EntryPoint::setAttribute(const std::string &line, size_t lc)
{
size_t pos = 0;
size_t last;
std::string token = getNextToken(line, 0, &last, WHITESPACE);
if (token == "len") {
pos = last;
std::string varname = getNextToken(line, pos, &last, WHITESPACE);
if (varname.size() == 0) {
fprintf(stderr, "ERROR: %u: Missing variable name in 'len' attribute\n", (unsigned int)lc);
return -1;
}
Var * v = var(varname);
if (v == NULL) {
fprintf(stderr, "ERROR: %u: variable %s is not a parameter of %s\n",
(unsigned int)lc, varname.c_str(), name().c_str());
return -2;
}
// set the size expression into var
pos = last;
v->setLenExpression(line.substr(pos));
} else if (token == "dir") {
pos = last;
std::string varname = getNextToken(line, pos, &last, WHITESPACE);
if (varname.size() == 0) {
fprintf(stderr, "ERROR: %u: Missing variable name in 'dir' attribute\n", (unsigned int)lc);
return -1;
}
Var * v = var(varname);
if (v == NULL) {
fprintf(stderr, "ERROR: %u: variable %s is not a parameter of %s\n",
(unsigned int)lc, varname.c_str(), name().c_str());
return -2;
}
pos = last;
std::string pointerDirStr = getNextToken(line, pos, &last, WHITESPACE);
if (pointerDirStr.size() == 0) {
fprintf(stderr, "ERROR: %u: missing pointer directions\n", (unsigned int)lc);
return -3;
}
if (pointerDirStr == "out") {
v->setPointerDir(Var::POINTER_OUT);
} else if (pointerDirStr == "inout") {
v->setPointerDir(Var::POINTER_INOUT);
} else if (pointerDirStr == "in") {
v->setPointerDir(Var::POINTER_IN);
} else {
fprintf(stderr, "ERROR: %u: unknow pointer direction %s\n", (unsigned int)lc, pointerDirStr.c_str());
}
} else if (token == "var_flag") {
pos = last;
std::string varname = getNextToken(line, pos, &last, WHITESPACE);
if (varname.size() == 0) {
fprintf(stderr, "ERROR: %u: Missing variable name in 'var_flag' attribute\n", (unsigned int)lc);
return -1;
}
Var * v = var(varname);
if (v == NULL) {
fprintf(stderr, "ERROR: %u: variable %s is not a parameter of %s\n",
(unsigned int)lc, varname.c_str(), name().c_str());
return -2;
}
pos = last;
std::string flag = getNextToken(line, pos, &last, WHITESPACE);
if (flag.size() == 0) {
fprintf(stderr, "ERROR: %u: missing flag\n", (unsigned int) lc);
return -3;
}
if (flag == "nullAllowed") {
if (v->isPointer()) {
v->setNullAllowed(true);
} else {
fprintf(stderr, "WARNING: %u: setting nullAllowed for non-pointer variable %s\n",
(unsigned int) lc, v->name().c_str());
}
} else {
fprintf(stderr, "WARNING: %u: unknow flag %s\n", (unsigned int)lc, flag.c_str());
}
} else if (token == "custom_pack") {
pos = last;
std::string varname = getNextToken(line, pos, &last, WHITESPACE);
if (varname.size() == 0) {
fprintf(stderr, "ERROR: %u: Missing variable name in 'custom_pack' attribute\n", (unsigned int)lc);
return -1;
}
Var * v = var(varname);
if (v == NULL) {
fprintf(stderr, "ERROR: %u: variable %s is not a parameter of %s\n",
(unsigned int)lc, varname.c_str(), name().c_str());
return -2;
}
// set the size expression into var
pos = last;
v->setPackExpression(line.substr(pos));
} else if (token == "flag") {
pos = last;
std::string flag = getNextToken(line, pos, &last, WHITESPACE);
if (flag.size() == 0) {
fprintf(stderr, "ERROR: %u: missing flag\n", (unsigned int) lc);
return -4;
}
if (flag == "unsupported") {
setUnsupported(true);
} else if (flag == "custom_decoder") {
setCustomDecoder(true);
} else {
fprintf(stderr, "WARNING: %u: unknown flag %s\n", (unsigned int)lc, flag.c_str());
}
} else {
fprintf(stderr, "WARNING: %u: unknown attribute %s\n", (unsigned int)lc, token.c_str());
}
return 0;
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __EntryPoint__H__
#define __EntryPoint__H__
#include <string>
#include <vector>
#include <stdio.h>
#include "Var.h"
//---------------------------------------------------
typedef std::vector<Var> VarsArray;
class EntryPoint {
public:
EntryPoint();
virtual ~EntryPoint();
bool parse(unsigned int lc, const std::string & str);
void reset(); // reset the class to empty;
void print(FILE *fp = stdout, bool newline = true,
const std::string & name_suffix = std::string(""),
const std::string & name_prefix = std::string(""),
const std::string & ctx_param = std::string("")) const;
const std::string & name() const { return m_name; }
VarsArray & vars() { return m_vars; }
Var & retval() { return m_retval; }
Var * var(const std::string & name);
bool hasPointers();
bool unsupported() const { return m_unsupported; }
void setUnsupported(bool state) { m_unsupported = state; }
bool customDecoder() { return m_customDecoder; }
void setCustomDecoder(bool state) { m_customDecoder = state; }
int setAttribute(const std::string &line, size_t lc);
private:
enum { PR_RETVAL = 0, PR_NAME, PR_VARS, PR_DONE } prState;
std::string m_name;
Var m_retval;
VarsArray m_vars;
bool m_unsupported;
bool m_customDecoder;
void err(unsigned int lc, const char *msg) {
fprintf(stderr, "line %d: %s\n", lc, msg);
}
};
#endif

View File

@@ -0,0 +1,292 @@
Introduction:
The emugen tool is a tool to generate a wire protocol implementation
based on provided API. The tool generates c++ encoder code that takes
API calls and encodes them into the wire and decoder code that decodes
the wire stream and calls server matching API.
The following paragraphs includes the following:
* Wire Protocol Description
* Input files description & format
* Generated code description.
Note: In this document, the caller is referred to as Encoder or Client
and the callee is referred to as the Decoder or Server. These terms
are used interchangeably by the context.
Wire Protocol packet structure:
A general Encoder->Decoder packet is structured as following:
struct Packet {
unsigned int opcode;
unsigned int packet_len;
… parameter 1
… parameter 2
};
A general Decoder->Encoder reply is expected to be received in the
context of the call that triggered it, thus it includes the reply
data only, with no context headers. In precise term terms, a reply
packet will look like:
struct {
...// reply data
};
consider the following function call:
int foo(int p1, short s1)
will be encoded into :
{
101, // foo opcode
14, // sizeof(opcode) + sizeof(packet_len) + sizeof(int) + sizeof(short)
p1, // 4 bytes
s1 // 4 bytes
}
Since foo returns value, the caller is expected to read back the return packet from the server->client stream. The return value in this example is in thus the return packet is:
{
int retval;
}
Pointer decoding:
The wire protocol also allows exchanging of pointer data
(arrays). Pointers are defined with directions:
in : Data is sent from the caller to the calle
out: Data is sent from the callee to the caller
in_out: data is sent from the caller and return in place.
in and in_out encoded with their len:
{
unsinged int pointer_data_len;
unsigned char data[pointer_data_len];… // pointer data
}
out pointers are encoded by data length only:
{
unsigned int pointer_data_len;
}
out and in_out pointers data is returned in the return
packet. For example, consider the following call:
int foo(int n, int *ptr); // assume that data is in_out pointer which contains n ints
The caller packet will have the following form:
{
101, // foo opcode
xx, sizeof(opcode) + sizeof(datalen) + sizeof(int) + sizeof(unsigned int) + n * sizeof(int);
n, // the n parameter
n * sizeof(int), // size of the data in ptr
… // n* sizeof(int) bytes
}
The return packet is;
{
…. // n * sizeof(int) bytes of data return in ptr
retval // sizeof(int) - the return value of the function;
}
Endianess
The Wire protocol is designed to impose minimum overhead on the client
side. Thus, the data endianness that is sent across the wire is
determined by the client side. It is up to the server side to
determine the client endianess and marshal the packets as required.
Emugen input files - protocol specification
The protocol generated by emugen consists of two input files:
1. basename.in - A sepcification of the protocol RPC procedures. This
part of the specification is expected to be generated automatically
from c/c++ header files or similar.
basename is the basename for the protocol and will be used to prefix
the files that are generated for this protocol. A line in the .in
file has the following format:
[prefix](retvalType, FuncName, <param type> [param name],...)
where
retvalType - The function return value type
FuncName - function name
<param type> mandatory parameter type
[param name] - optional parameter name
Examples:
GL_ENTRY(void, glVertex1f, float v)
XXX(int *, foo, int n, float, short)
XXX(void, glFlush, void)
Note: Empty lines in the file are ignored. A line starts with # is a comment
2. basename.attrib - Attributes information of the API.
This file includes additional flags, pointers datalen information and
global attributes of the protocol. For uptodate format of the file,
please refer to the specification file in the project source
tree. The format of the .attrib file is described below.
3. basename.types - Types information
This files describes the types that are described by the API. A type
is defined as follows:
<type name> <size in bits> <print format string>
where:
<type name> is the name of the type as described in the API
<size in bits> 0, 8, 16, 32 sizes are accepted
<print format string> a string to format the value of the type, as acceted by printf(3)
example:
GLint 32 %d
Encoder generated code files
In order to generate the encoder files, one should run the emugen
tool as follows:
emugen -i <input directory> -E <encoder files output directory> <basename>
where:
<input directory> containes the api specification files (basename.in + basename.attrib)
<encoder directory> - a directory name to generate the encoder output files
basename - The basename for the api.
Assuming the basename is api, The following files are generated:
api_opcodes.h - defines the protocol opcodes. The first opcode value
is 0, unless defined otherwise in the .attrib file
api_entry.cpp - defines entry points for the functions that are
defined by the protocol. this File also includes a function call
setContextAccessor(void *(*f)()). This function should be used to
provide a callback function that is used by the functions to access
the encoder context. For example, such callback could fetch the
context from a Thread Local Storage (TLS) location.
api_client_proc.h - type defintions for the protocol procedures.
api_client_context.h - defines the client side dispatch table data
structure that stores the encoding functions. This data structure also
includes accessors methods such that library user can override
default entries for special case handling.
api_enc.h - This header file defines the encoder data strcuture. The
encoder data structure inherits its functionality from the
client_context class above and adds encoding and streaming
functionality.
api_enc.cpp - Encoder implementation.
5.1.2.2 Decoder generated files
In order to generate the decoder files, one should run the emugen
tool as follows:
emugen -i <input directory> -D <decoder files output directory> basename
where:
<input directory> containes the api specification files (basename.in + basename.attrib)
<decoder directory> - a directory name to generate the decoder output files
basename - The basename for the api.
With resepct to the example above, Emugen will generate the following
files:
api_opcodes.h - Protocol opcodes
api_server_proc.h - type definitions for the server side procedures
api_server_context.h - dispatch table the encoder functions
api_dec.h - Decoder header file
api_dec.cpp - Decoder implementation. In addtion, this file includes
an intiailization function that uses a user provided callback to
initialize the API server implementation. An example for such
initialization is loading a set of functions from a shared library
module.
.attrib file format description:
The .attrib file is an input file to emugen and is used to provide
additional information that is required for the code generation.
The file format is as follows:
a line that starts with # is ignored (comment)
a empty line just whitespace of (" " "\t" "\n") is ignored.
The file is divided into 'sections', each describes a specific API
function call. A section starts with the name of the function in
column 0.
A section that starts with the reserved word 'GLOBAL' provides global
attributes.
below are few sections examples:
GLOBAL
encoder_headers string.h kuku.h
glVertex3fv
len data (size)
glTexImage2D
len pixels (pixels == NULL? 0 : (format_pixel_size(internalformat) * width * height * type_size(type)))
Global section flags description:
base_opcode
set the base opcode value for this api
format: base_opcode 100
encoder_headers
a list of headers that will be included in the encoder header file
format: encoder_headers <stdio.h> "kuku.h"
client_context_headers
a list of headers that will be included in the client context header file
format: client_context_headers <stdio.h> "kuku.h"
decoder_headers
a list of headers that will be included in the decoder header file
format: decoder_headers <stdio.h> "kuku.h"
server_context_headers
a list of headers that will be included in the server context header file
format: server_context_headers <stdio.h> "kuku.h"
Entry point flags description:
len
desciption : provide an expression to calcualte an expression data len
format: len <var name> <c expression that calcluates the data len>
custom_pack
description: provide an expression to pack data into the stream.
format: custom_pack <var name> <c++ expression that pack data from var into the stream>
The stream is represented by a (unsigned char *)ptr. The expression may also refer
to other function parameters. In addition, the expression may refer to 'void *self' which
is the encoding context as provided by the caller.
dir
description : set a pointer direction (in - for data that goes
to the codec, out from data that returns from the codec.
format: dir <varname> <[in | out | inout]>
var_flag
description : set variable flags
format: var_flag <varname> < nullAllowed | ... >
flag
description: set entry point flag;
format: flag < unsupported | ... >
supported flags are:
unsupported - The encoder side implementation is pointed to "unsuppored reporting function".
custom_decoder - The decoder is expected to be provided with
custom implementation. The call to the
deocder function includes a pointer to the
context

View File

@@ -0,0 +1,135 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "TypeFactory.h"
#include "VarType.h"
#include <string>
#include <map>
#include <stdio.h>
#include <stdlib.h>
#include "strUtils.h"
TypeFactory * TypeFactory::m_instance = NULL;
static Var0 g_var0;
static Var8 g_var8;
static Var16 g_var16;
static Var32 g_var32;
typedef std::map<std::string, VarType> TypeMap;
static TypeMap g_varMap;
static bool g_initialized = false;
static int g_typeId = 0;
static VarConverter * getVarConverter(int size)
{
VarConverter *v = NULL;
switch(size) {
case 0: v = &g_var0; break;
case 8: v = &g_var8; break;
case 16: v = &g_var16; break;
case 32: v = &g_var32; break;
}
return v;
}
#define ADD_TYPE(name, size, printformat) \
g_varMap.insert(std::pair<std::string, VarType>(name, VarType(g_typeId++, name, &g_var##size,printformat)));
void TypeFactory::initBaseTypes()
{
g_initialized = true;
ADD_TYPE("UNKNOWN", 0, "0x%x");
ADD_TYPE("void", 0, "0x%x");
ADD_TYPE("char", 8, "%c");
ADD_TYPE("int", 32, "%d");
ADD_TYPE("float", 32, "%d");
ADD_TYPE("short", 16, "%d");
}
int TypeFactory::initFromFile(const std::string &filename)
{
if (!g_initialized) {
initBaseTypes();
}
FILE *fp = fopen(filename.c_str(), "rt");
if (fp == NULL) {
perror(filename.c_str());
return -1;
}
char line[1000];
int lc = 0;
while(fgets(line, sizeof(line), fp) != NULL) {
lc++;
std::string str = trim(line);
if (str.size() == 0 || str.at(0) == '#') {
continue;
}
size_t pos = 0, last;
std::string name;
name = getNextToken(str, pos, &last, WHITESPACE);
if (name.size() == 0) {
fprintf(stderr, "Error: %d : missing type name\n", lc);
return -2;
}
pos = last + 1;
std::string size;
size = getNextToken(str, pos, &last, WHITESPACE);
if (size.size() == 0) {
fprintf(stderr, "Error: %d : missing type width\n", lc);
return -2;
}
pos = last + 1;
std::string printString;
printString = getNextToken(str, pos, &last, WHITESPACE);
if (printString.size() == 0) {
fprintf(stderr, "Error: %d : missing print-string\n", lc);
return -2;
}
VarConverter *v = getVarConverter(atoi(size.c_str()));
if (v == NULL) {
fprintf(stderr, "Error: %d : unknown var width: %d\n", lc, atoi(size.c_str()));
return -1;
}
if (getVarTypeByName(name)->id() != 0) {
fprintf(stderr,
"Warining: %d : type %s is already known, definition in line %d is taken\n",
lc, name.c_str(), lc);
}
g_varMap.insert(std::pair<std::string, VarType>(name, VarType(g_typeId++, name, v ,printString)));
}
g_initialized = true;
return 0;
}
const VarType * TypeFactory::getVarTypeByName(const std::string & type)
{
if (!g_initialized) {
initBaseTypes();
}
TypeMap::iterator i = g_varMap.find(type);
if (i == g_varMap.end()) {
i = g_varMap.find("UNKNOWN");
}
return &(i->second);
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __TYPE__FACTORY__H__
#define __TYPE__FACTORY__H__
#include <string>
#include "VarType.h"
class TypeFactory {
public:
static TypeFactory *instance() {
if (m_instance == NULL) {
m_instance = new TypeFactory;
}
return m_instance;
}
const VarType * getVarTypeByName(const std::string &type);
int initFromFile(const std::string &filename);
private:
static TypeFactory *m_instance;
void initBaseTypes();
TypeFactory() {}
};
#endif

View File

@@ -0,0 +1,94 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __VAR__H__
#define __VAR__H__
#include "VarType.h"
#include <string>
#include <stdio.h>
class Var {
public:
// pointer data direction - from the client point of view.
typedef enum { POINTER_OUT = 0x1, POINTER_IN = 0x2, POINTER_INOUT = 0x3 } PointerDir;
Var() :
m_name(""),
m_type(NULL),
m_pointer(false),
m_lenExpression(""),
m_pointerDir(POINTER_IN),
m_nullAllowed(false),
m_packExpression("")
{
}
Var(const std::string & name,
const VarType * vartype,
bool isPointer,
const std::string & lenExpression,
PointerDir dir,
const std::string &packExpression) :
m_name(name),
m_type(const_cast<VarType *>(vartype)),
m_pointer(isPointer),
m_lenExpression(lenExpression),
m_pointerDir(dir),
m_nullAllowed(false),
m_packExpression(packExpression)
{
}
void init(const std::string name, const VarType * vartype,
bool isPointer, std::string lenExpression,
PointerDir dir, std::string packExpression) {
m_name = name;
m_type = vartype;
m_pointer = isPointer;
m_lenExpression = lenExpression;
m_packExpression = packExpression;
m_pointerDir = dir;
m_nullAllowed = false;
}
const std::string & name() const { return m_name; }
const VarType * type() const { return m_type; }
bool isPointer() const { return m_pointer; }
bool isVoid() const { return ((m_type->bytes() == 0) && (m_pointer == false)); }
const std::string & lenExpression() const { return m_lenExpression; }
const std::string & packExpression() const { return(m_packExpression); }
void setLenExpression(const std::string & lenExpression) { m_lenExpression = lenExpression; }
void setPackExpression(const std::string & packExpression) { m_packExpression = packExpression; }
void setPointerDir(PointerDir dir) { m_pointerDir = dir; }
PointerDir pointerDir() { return m_pointerDir; }
void setNullAllowed(bool state) { m_nullAllowed = state; }
bool nullAllowed() const { return m_nullAllowed; }
void printType(FILE *fp) { fprintf(fp, "%s%s", m_type->name().c_str(), m_pointer ? "*" : ""); }
void printTypeName(FILE *fp) { printType(fp); fprintf(fp, " %s", m_name.c_str()); }
private:
std::string m_name;
const VarType * m_type;
bool m_pointer; // is this variable a pointer;
std::string m_lenExpression; // an expression to calcualte a pointer data size
PointerDir m_pointerDir;
bool m_nullAllowed;
std::string m_packExpression; // an expression to pack data into the stream
};
#endif

View File

@@ -0,0 +1,76 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __VARTYPE__H__
#define __VARTYPE__H__
#include <string>
class VarConverter {
public:
VarConverter(size_t bytes) : m_bytes(bytes) {}
size_t bytes() const { return m_bytes; }
private:
size_t m_bytes;
};
class Var8 : public VarConverter {
public:
Var8() : VarConverter(1) {}
};
class Var16 : public VarConverter {
public:
Var16() : VarConverter(2) {}
};
class Var32 : public VarConverter {
public:
Var32() : VarConverter(4) {}
};
class Var0 : public VarConverter {
public:
Var0() : VarConverter(0) {}
};
class VarType {
public:
VarType() :
m_id(0), m_name("default_constructed"), m_converter(NULL), m_printFomrat("0x%x")
{
}
VarType(size_t id, const std::string & name, const VarConverter * converter, const std::string & printFormat ) :
m_id(id), m_name(name), m_converter(const_cast<VarConverter *>(converter)), m_printFomrat(printFormat)
{
}
~VarType()
{
}
const std::string & name() const { return m_name; }
const std::string & printFormat() const { return m_printFomrat; }
size_t bytes() const { return m_converter->bytes(); }
size_t id() const { return m_id; }
private:
size_t m_id;
std::string m_name;
VarConverter * m_converter;
std::string m_printFomrat;
};
#endif

View File

@@ -0,0 +1,24 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _ERRORS_H_
#define _ERRORS_H_
#define BAD_USAGE -1
#define BAD_SPEC_FILE -2
#define BAD_TYPES_FILE -3
#define BAD_ATTRIBUTES_FILE -4
#endif

View File

@@ -0,0 +1,147 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
#include <stdlib.h>
#include "errors.h"
#include "EntryPoint.h"
#include "strUtils.h"
#include "ApiGen.h"
#include "TypeFactory.h"
const std::string SPEC_EXTENSION = std::string(".in");
const std::string ATTRIB_EXTENSION = std::string(".attrib");
const std::string TYPES_EXTENTION = std::string(".types");
void usage(const char *filename)
{
fprintf(stderr, "Usage: %s [options] <base name>\n", filename);
fprintf(stderr, "\t-h: This message\n");
fprintf(stderr, "\t-E <dir>: generate encoder into dir\n");
fprintf(stderr, "\t-D <dir>: generate decoder into dir\n");
fprintf(stderr, "\t-i: input dir, local directory by default\n");
fprintf(stderr, "\t-T : generate attribute template into the input directory\n\t\tno other files are generated\n");
}
int main(int argc, char *argv[])
{
std::string encoderDir = "";
std::string decoderDir = "";
std::string inDir = ".";
bool generateAttributesTemplate = false;
int c;
while((c = getopt(argc, argv, "TE:D:i:h")) != -1) {
switch(c) {
case 'T':
generateAttributesTemplate = true;
break;
case 'h':
usage(argv[0]);
exit(0);
break;
case 'E':
encoderDir = std::string(optarg);
break;
case 'D':
decoderDir = std::string(optarg);
break;
case 'i':
inDir = std::string(optarg);
break;
case ':':
fprintf(stderr, "Missing argument !!\n");
// fall through
default:
usage(argv[0]);
exit(0);
}
}
if (optind >= argc) {
fprintf(stderr, "Usage: %s [options] <base name> \n", argv[0]);
return BAD_USAGE;
}
if (encoderDir.size() == 0 && decoderDir.size() == 0 && generateAttributesTemplate == false) {
fprintf(stderr, "No output specified - aborting\n");
return BAD_USAGE;
}
std::string baseName = std::string(argv[optind]);
ApiGen apiEntries(baseName);
// init types;
std::string typesFilename = inDir + "/" + baseName + TYPES_EXTENTION;
if (TypeFactory::instance()->initFromFile(typesFilename) < 0) {
fprintf(stderr, "missing or error reading types file: %s...ignored\n", typesFilename.c_str());
}
std::string filename = inDir + "/" + baseName + SPEC_EXTENSION;
if (apiEntries.readSpec(filename) < 0) {
perror(filename.c_str());
return BAD_SPEC_FILE;
}
if (generateAttributesTemplate) {
apiEntries.genAttributesTemplate(inDir + "/" + baseName + ATTRIB_EXTENSION);
exit(0);
}
std::string attribFileName = inDir + "/" + baseName + ATTRIB_EXTENSION;
if (apiEntries.readAttributes(attribFileName) < 0) {
perror(attribFileName.c_str());
fprintf(stderr, "failed to parse attributes\n");
exit(1);
}
if (encoderDir.size() != 0) {
apiEntries.genOpcodes(encoderDir + "/" + baseName + "_opcodes.h");
apiEntries.genContext(encoderDir + "/" + baseName + "_client_context.h", ApiGen::CLIENT_SIDE);
apiEntries.genProcTypes(encoderDir + "/" + baseName + "_client_proc.h", ApiGen::CLIENT_SIDE);
apiEntries.genClientEntryPoints(encoderDir + "/" + baseName + "_entry.cpp");
apiEntries.genEncoderHeader(encoderDir + "/" + baseName + "_enc.h");
apiEntries.genEncoderImpl(encoderDir + "/" + baseName + "_enc.cpp");
}
if (decoderDir.size() != 0) {
//apiEntries.genEntryPoints(decoderDir + "/" + baseName + "_entry.cpp", baseName);
apiEntries.genOpcodes(decoderDir + "/" + baseName + "_opcodes.h");
apiEntries.genProcTypes(decoderDir + "/" + baseName + "_server_proc.h", ApiGen::SERVER_SIDE);
apiEntries.genContext(decoderDir + "/" + baseName + "_server_context.h", ApiGen::SERVER_SIDE);
apiEntries.genDecoderHeader(decoderDir + "/" + baseName + "_dec.h");
apiEntries.genDecoderImpl(decoderDir + "/" + baseName + "_dec.cpp");
// generate the encoder type;
}
#ifdef DEBUG_DUMP
int withPointers = 0;
printf("%d functions found\n", int(apiEntries.size()));
for (int i = 0; i < apiEntries.size(); i++) {
if (apiEntries[i].hasPointers()) {
withPointers++;
apiEntries[i].print();
}
}
fprintf(stdout, "%d entries has poitners\n", withPointers);
#endif
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "strUtils.h"
using namespace std;
std::string getNextToken(const std::string & str, size_t pos, size_t * last, const std::string & delim)
{
if (str.size() == 0 || pos >= str.size()) return "";
pos = str.find_first_not_of(WHITESPACE, pos);
if (pos == std::string::npos) return "";
*last = str.find_first_of(delim, pos);
if (*last == std::string::npos) *last = str.size();
std::string retval = str.substr(pos, *last - pos);
retval = trim(retval);
return retval;
}
std::string trim(const string & str)
{
string result;
string::size_type start = str.find_first_not_of(WHITESPACE, 0);
string::size_type end = str.find_last_not_of(WHITESPACE);
if (start == string::npos || end == string::npos) {
result = string("");
} else {
result = str.substr(start, end - start + 1);
}
return result;
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef STR_UTILS_H_
#define STR_UTILS_H_
#include <string>
#include <sstream>
#define WHITESPACE " \t\n"
std::string trim(const std::string & str);
std::string getNextToken(const std::string & str, size_t pos, size_t * last, const std::string & delim);
template <class T> std::string inline toString(const T& t) {
std::stringstream ss;
ss << t;
return ss.str();
}
#endif