aboutsummaryrefslogtreecommitdiffstats
path: root/emulator/opengl/host/tools/emugen/ApiGen.cpp
diff options
context:
space:
mode:
authorJesse Hall <jessehall@google.com>2012-04-16 12:49:39 -0700
committerJesse Hall <jessehall@google.com>2012-04-16 15:54:18 -0700
commitce6c3389061fb9fcdefc94fab2044a8e11600b52 (patch)
treefedd1a11cbd21ec14bf00be83b8712054f8c5506 /emulator/opengl/host/tools/emugen/ApiGen.cpp
parentad0111a77b0f0908cc945dc6e8e8949b75cb8886 (diff)
downloadsdk-ce6c3389061fb9fcdefc94fab2044a8e11600b52.zip
sdk-ce6c3389061fb9fcdefc94fab2044a8e11600b52.tar.gz
sdk-ce6c3389061fb9fcdefc94fab2044a8e11600b52.tar.bz2
Move emulator GLES from development.git to sdk.git
The emulator GLES support has two interfaces: a host shared library interface used by QEMU, and a protocol between the platform and the host. The host library interface is not versioned; QEMU and the GLES renderer must match. The protocol on the other hand must be backwards compatible: a new GLES renderer must support an older platform image. Thus for branching purposes it makes more sense to put the GLES renderer in sdk.git, which is branched along with qemu.git for SDK releases. Platform images will be built against the protocol version in the platform branch of sdk.git. Change-Id: I2c3bce627ecfd0a4b3e688d1839fe10755a21e58
Diffstat (limited to 'emulator/opengl/host/tools/emugen/ApiGen.cpp')
-rw-r--r--emulator/opengl/host/tools/emugen/ApiGen.cpp1081
1 files changed, 1081 insertions, 0 deletions
diff --git a/emulator/opengl/host/tools/emugen/ApiGen.cpp b/emulator/opengl/host/tools/emugen/ApiGen.cpp
new file mode 100644
index 0000000..bf2d244
--- /dev/null
+++ b/emulator/opengl/host/tools/emugen/ApiGen.cpp
@@ -0,0 +1,1081 @@
+/*
+* 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>
+
+/* Define this to 1 to enable support for the 'isLarge' variable flag
+ * that instructs the encoder to send large data buffers by a direct
+ * write through the pipe (i.e. without copying it into a temporary
+ * buffer. This has definite performance benefits when using a QEMU Pipe.
+ *
+ * Set to 0 otherwise.
+ */
+#define WITH_LARGE_SUPPORT 1
+
+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);
+
+ const char* basename = m_basename.c_str();
+
+ fprintf(fp, "#ifndef __%s_%s_proc_t_h\n", basename, sideString(side));
+ fprintf(fp, "#define __%s_%s_proc_t_h\n", basename, sideString(side));
+ fprintf(fp, "\n\n");
+ fprintf(fp, "\n#include \"%s_types.h\"\n",basename);
+ fprintf(fp, "#ifndef %s_APIENTRY\n",basename);
+ fprintf(fp, "#define %s_APIENTRY \n",basename);
+ fprintf(fp, "#endif\n");
+
+
+ for (size_t i = 0; i < size(); i++) {
+ EntryPoint *e = &at(i);
+
+ fprintf(fp, "typedef ");
+ e->retval().printType(fp);
+ fprintf(fp, " (%s_APIENTRY *%s_%s_proc_t) (", basename, 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::genFuncTable(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_ftable_t_h\n", m_basename.c_str(), sideString(side));
+ fprintf(fp, "#define __%s_%s_ftable_t_h\n", m_basename.c_str(), sideString(side));
+ fprintf(fp, "\n\n");
+ fprintf(fp, "static struct _%s_funcs_by_name {\n", m_basename.c_str());
+ fprintf(fp,
+ "\tconst char *name;\n" \
+ "\tvoid *proc;\n" \
+ "} %s_funcs_by_name[] = {\n", m_basename.c_str());
+
+
+ for (size_t i = 0; i < size(); i++) {
+ EntryPoint *e = &at(i);
+ if (e->notApi()) continue;
+ fprintf(fp, "\t{\"%s\", (void*)%s},\n", e->name().c_str(), e->name().c_str());
+ }
+ fprintf(fp, "};\n");
+ fprintf(fp, "static int %s_num_funcs = sizeof(%s_funcs_by_name) / sizeof(struct _%s_funcs_by_name);\n",
+ m_basename.c_str(), m_basename.c_str(), m_basename.c_str());
+ 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 || side == WRAPPER_SIDE) {
+ fprintf(fp, "\n\ttypedef %s_%s_context_t *CONTEXT_ACCESSOR_TYPE(void);\n",
+ m_basename.c_str(), sideString(side));
+ fprintf(fp, "\tstatic void setContextAccessor(CONTEXT_ACCESSOR_TYPE *f);\n");
+ }
+
+ // init function
+ fprintf(fp, "\tint initDispatchByName( void *(*getProc)(const char *name, void *userData), void *userData);\n");
+
+ //client site set error virtual func
+ if (side == CLIENT_SIDE) {
+ fprintf(fp, "\tvirtual void setError(unsigned int error){};\n");
+ fprintf(fp, "\tvirtual unsigned int getError(){ return 0; };\n");
+ }
+
+ fprintf(fp, "};\n");
+
+ fprintf(fp, "\n#endif\n");
+ fclose(fp);
+ return 0;
+}
+
+int ApiGen::genEntryPoints(const std::string & filename, SideType side)
+{
+
+ if (side != CLIENT_SIDE && side != WRAPPER_SIDE) {
+ fprintf(stderr, "Entry points are only defined for Client and Wrapper components\n");
+ return -999;
+ }
+
+
+ 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(side));
+ fprintf(fp, "\n");
+
+ fprintf(fp, "#ifndef GL_TRUE\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, "#endif\n");
+
+ fprintf(fp, "#ifndef GET_CONTEXT\n");
+ fprintf(fp, "static %s_%s_context_t::CONTEXT_ACCESSOR_TYPE *getCurrentContext = NULL;\n",
+ m_basename.c_str(), sideString(side));
+
+ fprintf(fp,
+ "void %s_%s_context_t::setContextAccessor(CONTEXT_ACCESSOR_TYPE *f) { getCurrentContext = f; }\n",
+ m_basename.c_str(), sideString(side));
+ fprintf(fp, "#define GET_CONTEXT %s_%s_context_t * ctx = getCurrentContext() \n",
+ m_basename.c_str(), sideString(side));
+ fprintf(fp, "#endif\n\n");
+
+
+ for (size_t i = 0; i < size(); i++) {
+ EntryPoint *e = &at(i);
+ e->print(fp);
+ fprintf(fp, "{\n");
+ fprintf(fp, "\tGET_CONTEXT; \n");
+
+ bool shouldReturn = !e->retval().isVoid();
+ bool shouldCallWithContext = (side == CLIENT_SIDE);
+ //param check
+ if (shouldCallWithContext) {
+ for (size_t j=0; j<e->vars().size(); j++) {
+ if (e->vars()[j].paramCheckExpression() != "")
+ fprintf(fp, "\t%s\n", e->vars()[j].paramCheckExpression().c_str());
+ }
+ }
+ fprintf(fp, "\t %sctx->%s(%s",
+ shouldReturn ? "return " : "",
+ e->name().c_str(),
+ shouldCallWithContext ? "ctx" : "");
+ size_t nvars = e->vars().size();
+
+ for (size_t j = 0; j < nvars; j++) {
+ if (!e->vars()[j].isVoid()) {
+ fprintf(fp, "%s %s",
+ j != 0 || shouldCallWithContext ? "," : "",
+ 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;
+}
+
+// Format the byte length expression for a given variable into a user-provided buffer
+// If the variable type is not a pointer, this is simply its size as a decimal constant
+// If the variable is a pointer, this will be an expression provided by the .attrib file
+// through the 'len' attribute.
+//
+// Returns 1 if the variable is a pointer, 0 otherwise
+//
+static int getVarEncodingSizeExpression(Var& var, EntryPoint* e, char* buff, size_t bufflen)
+{
+ int ret = 0;
+ if (!var.isPointer()) {
+ snprintf(buff, bufflen, "%u", (unsigned int) var.type()->bytes());
+ } else {
+ ret = 1;
+ const char* lenExpr = var.lenExpression().c_str();
+ const char* varname = var.name().c_str();
+ if (e != NULL && lenExpr[0] == '\0') {
+ fprintf(stderr, "%s: data len is undefined for '%s'\n",
+ e->name().c_str(), varname);
+ }
+ if (var.nullAllowed()) {
+ snprintf(buff, bufflen, "((%s != NULL) ? %s : 0)", varname, lenExpr);
+ } else {
+ snprintf(buff, bufflen, "%s", lenExpr);
+ }
+ }
+ return ret;
+}
+
+static int writeVarEncodingSize(Var& var, FILE* fp)
+{
+ int ret = 0;
+ if (!var.isPointer()) {
+ fprintf(fp, "%u", (unsigned int) var.type()->bytes());
+ } else {
+ ret = 1;
+ fprintf(fp, "__size_%s", var.name().c_str());
+ }
+ return ret;
+}
+
+
+
+static void writeVarEncodingExpression(Var& var, FILE* fp)
+{
+ const char* varname = var.name().c_str();
+
+ if (var.isPointer()) {
+ // encode a pointer header
+ fprintf(fp, "\t*(unsigned int *)(ptr) = __size_%s; ptr += 4;\n", varname);
+
+ Var::PointerDir dir = var.pointerDir();
+ if (dir == Var::POINTER_INOUT || dir == Var::POINTER_IN) {
+ if (var.nullAllowed()) {
+ fprintf(fp, "\tif (%s != NULL) ", varname);
+ } else {
+ fprintf(fp, "\t");
+ }
+
+ if (var.packExpression().size() != 0) {
+ fprintf(fp, "%s;", var.packExpression().c_str());
+ } else {
+ fprintf(fp, "memcpy(ptr, %s, __size_%s);",
+ varname, varname);
+ }
+
+ fprintf(fp, "ptr += __size_%s;\n", varname);
+ }
+ } else {
+ // encode a non pointer variable
+ if (!var.isVoid()) {
+ fprintf(fp, "\t*(%s *) (ptr) = %s; ptr += %u;\n",
+ var.type()->name().c_str(), varname,
+ (uint) var.type()->bytes());
+ }
+ }
+}
+
+#if WITH_LARGE_SUPPORT
+static void writeVarLargeEncodingExpression(Var& var, FILE* fp)
+{
+ const char* varname = var.name().c_str();
+
+ fprintf(fp, "\tstream->writeFully(&__size_%s,4);\n", varname);
+ if (var.nullAllowed()) {
+ fprintf(fp, "\tif (%s != NULL) ", varname);
+ } else {
+ fprintf(fp, "\t");
+ }
+ if (var.writeExpression() != "") {
+ fprintf(fp, "%s", var.writeExpression().c_str());
+ } else {
+ fprintf(fp, "stream->writeFully(%s, __size_%s)", varname, varname);
+ }
+ fprintf(fp, ";\n");
+}
+#endif /* WITH_LARGE_SUPPORT */
+
+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\tALOGE(\"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\tDBG(\">>>> %s\\n\");\n", e->name().c_str());
+ fprintf(fp, "\n\t%s *ctx = (%s *)self;\n",
+ classname.c_str(),
+ classname.c_str());
+ fprintf(fp, "\tIOStream *stream = ctx->m_stream;\n\n");
+ VarsArray & evars = e->vars();
+ size_t maxvars = evars.size();
+ size_t j;
+
+ char buff[256];
+
+ // Define the __size_XXX variables that contain the size of data
+ // associated with pointers.
+ for (j = 0; j < maxvars; j++) {
+ Var& var = evars[j];
+
+ if (!var.isPointer())
+ continue;
+
+ const char* varname = var.name().c_str();
+ fprintf(fp, "\tconst unsigned int __size_%s = ", varname);
+
+ getVarEncodingSizeExpression(var, e, buff, sizeof(buff));
+ fprintf(fp, "%s;\n", buff);
+ }
+
+#if WITH_LARGE_SUPPORT
+ // We need to take care of 'isLarge' variable in a special way
+ // Anything before an isLarge variable can be packed into a single
+ // buffer, which is then commited. Each isLarge variable is a pointer
+ // to data that can be written to directly through the pipe, which
+ // will be instant when using a QEMU pipe
+
+ size_t nvars = 0;
+ size_t npointers = 0;
+
+ // First, compute the total size, 8 bytes for the opcode + payload size
+ fprintf(fp, "\t unsigned char *ptr;\n");
+ fprintf(fp, "\t const size_t packetSize = 8");
+
+ for (j = 0; j < maxvars; j++) {
+ fprintf(fp, " + ");
+ npointers += writeVarEncodingSize(evars[j], fp);
+ }
+ if (npointers > 0) {
+ fprintf(fp, " + %zu*4", npointers);
+ }
+ fprintf(fp, ";\n");
+
+ // We need to divide the packet into fragments. Each fragment contains
+ // either copied arguments to a temporary buffer, or direct writes for
+ // large variables.
+ //
+ // The first fragment must also contain the opcode+payload_size
+ //
+ nvars = 0;
+ while (nvars < maxvars || maxvars == 0) {
+
+ // Skip over non-large fields
+ for (j = nvars; j < maxvars; j++) {
+ if (evars[j].isLarge())
+ break;
+ }
+
+ // Write a fragment if needed.
+ if (nvars == 0 || j > nvars) {
+ const char* plus = "";
+
+ if (nvars == 0 && j == maxvars) {
+ // Simple shortcut for the common case where we don't have large variables;
+ fprintf(fp, "\tptr = stream->alloc(packetSize);\n");
+
+ } else {
+ // allocate buffer from the stream until the first large variable
+ fprintf(fp, "\tptr = stream->alloc(");
+ plus = "";
+
+ if (nvars == 0) {
+ fprintf(fp,"8"); plus = " + ";
+ }
+ if (j > nvars) {
+ npointers = 0;
+ for (j = nvars; j < maxvars && !evars[j].isLarge(); j++) {
+ fprintf(fp, "%s", plus); plus = " + ";
+ npointers += writeVarEncodingSize(evars[j], fp);
+ }
+ if (npointers > 0) {
+ fprintf(fp, "%s%zu*4", plus, npointers); plus = " + ";
+ }
+ }
+ fprintf(fp,");\n");
+ }
+
+ // encode packet header if needed.
+ if (nvars == 0) {
+ 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");
+ }
+
+ if (maxvars == 0)
+ break;
+
+ // encode non-large fields in this fragment
+ for (j = nvars; j < maxvars && !evars[j].isLarge(); j++) {
+ writeVarEncodingExpression(evars[j],fp);
+ }
+
+ // Ensure the fragment is commited if it is followed by a large variable
+ if (j < maxvars) {
+ fprintf(fp, "\tstream->flush();\n");
+ }
+ }
+
+ // If we have one or more large variables, write them directly.
+ // As size + data
+ for ( ; j < maxvars && evars[j].isLarge(); j++) {
+ writeVarLargeEncodingExpression(evars[j], fp);
+ }
+
+ nvars = j;
+ }
+
+#else /* !WITH_LARGE_SUPPORT */
+ size_t nvars = evars.size();
+ size_t npointers = 0;
+ fprintf(fp, "\t const size_t packetSize = 8");
+ for (size_t j = 0; j < nvars; j++) {
+ npointers += getVarEncodingSizeExpression(evars[j],e,buff,sizeof(buff));
+ fprintf(fp, " + %s", buff);
+ }
+ fprintf(fp, " + %u * 4;\n", (unsigned int) npointers);
+
+ // allocate buffer from the stream;
+ fprintf(fp, "\t unsigned char *ptr = 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++) {
+ writeVarEncodingExpression(evars[j], fp);
+ }
+#endif /* !WITH_LARGE_SUPPORT */
+
+ // 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) {
+ const char* varname = evars[j].name().c_str();
+ if (evars[j].nullAllowed()) {
+ fprintf(fp, "\tif (%s != NULL) ",varname);
+ } else {
+ fprintf(fp, "\t");
+ }
+ fprintf(fp, "stream->readback(%s, __size_%s);\n",
+ varname, varname);
+ }
+ }
+ }
+//XXX fprintf(fp, "\n\tDBG(\"<<<< %s\\n\");\n", e->name().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, "\tstream->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, "\n};\n\n");
+ fprintf(fp, "#endif\n");
+
+ fclose(fp);
+ return 0;
+}
+
+int ApiGen::genContextImpl(const std::string &filename, SideType side)
+{
+ FILE *fp = fopen(filename.c_str(), "wt");
+ if (fp == NULL) {
+ perror(filename.c_str());
+ return -1;
+ }
+ printHeader(fp);
+
+ std::string classname = m_basename + "_" + sideString(side) + "_context_t";
+ size_t n = size();
+ fprintf(fp, "\n\n#include <string.h>\n");
+ fprintf(fp, "#include \"%s_%s_context.h\"\n\n\n", m_basename.c_str(), sideString(side));
+ fprintf(fp, "#include <stdio.h>\n\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(side));
+
+ }
+ fprintf(fp, "\treturn 0;\n");
+ fprintf(fp, "}\n\n");
+ 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\n");
+ 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");
+
+ // 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\
+#ifdef CHECK_GL_ERROR \n\
+\tchar lastCall[256] = {0}; \n\
+#endif \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();
+ }
+
+ 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(%s)\\n\"", m_basename.c_str(), 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) {
+ if (v->nullAllowed()) {
+ fprintf(fp, "*((unsigned int *)(ptr + %s)) == 0 ? NULL : (%s)(ptr + %s + 4)",
+ varoffset.c_str(), v->type()->name().c_str(), varoffset.c_str());
+ } else {
+ 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 + *(tsize_t *)(ptr +" + varoffset + ")";
+ } else { // out 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%uSize", (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) {
+ if (v->nullAllowed()) {
+ fprintf(fp, "tmpPtr%uSize == 0 ? NULL : (%s)(tmpPtr%u)",
+ (uint) j, v->type()->name().c_str(), (uint) j);
+ } else {
+ 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, "#ifdef CHECK_GL_ERROR\n");
+ fprintf(fp, "\t\t\tsprintf(lastCall, \"%s\");\n", e->name().c_str());
+ fprintf(fp, "#endif\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");
+ if (strstr(m_basename.c_str(), "gl")) {
+ fprintf(fp, "#ifdef CHECK_GL_ERROR\n");
+ fprintf(fp, "\tint err = this->glGetError();\n");
+ fprintf(fp, "\tif (err) fprintf(stderr, \"%s Error: 0x%%X in %%s\\n\", err, lastCall);\n", m_basename.c_str());
+ fprintf(fp, "#endif\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;
+}
+