diff options
Diffstat (limited to 'emulator/opengl')
-rw-r--r-- | emulator/opengl/host/tools/emugen/Android.mk | 10 | ||||
-rw-r--r-- | emulator/opengl/host/tools/emugen/ApiGen.cpp | 771 | ||||
-rw-r--r-- | emulator/opengl/host/tools/emugen/ApiGen.h | 78 | ||||
-rw-r--r-- | emulator/opengl/host/tools/emugen/EntryPoint.cpp | 335 | ||||
-rw-r--r-- | emulator/opengl/host/tools/emugen/EntryPoint.h | 64 | ||||
-rw-r--r-- | emulator/opengl/host/tools/emugen/README | 292 | ||||
-rw-r--r-- | emulator/opengl/host/tools/emugen/TypeFactory.cpp | 135 | ||||
-rw-r--r-- | emulator/opengl/host/tools/emugen/TypeFactory.h | 37 | ||||
-rw-r--r-- | emulator/opengl/host/tools/emugen/Var.h | 94 | ||||
-rw-r--r-- | emulator/opengl/host/tools/emugen/VarType.h | 76 | ||||
-rw-r--r-- | emulator/opengl/host/tools/emugen/errors.h | 24 | ||||
-rw-r--r-- | emulator/opengl/host/tools/emugen/main.cpp | 147 | ||||
-rw-r--r-- | emulator/opengl/host/tools/emugen/strUtils.cpp | 49 | ||||
-rw-r--r-- | emulator/opengl/host/tools/emugen/strUtils.h | 33 |
14 files changed, 2145 insertions, 0 deletions
diff --git a/emulator/opengl/host/tools/emugen/Android.mk b/emulator/opengl/host/tools/emugen/Android.mk new file mode 100644 index 0000000..fcd7b24 --- /dev/null +++ b/emulator/opengl/host/tools/emugen/Android.mk @@ -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) diff --git a/emulator/opengl/host/tools/emugen/ApiGen.cpp b/emulator/opengl/host/tools/emugen/ApiGen.cpp new file mode 100644 index 0000000..ffa71d8 --- /dev/null +++ b/emulator/opengl/host/tools/emugen/ApiGen.cpp @@ -0,0 +1,771 @@ +/* +* 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 init( void *(*getProc)(const char *name));\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::init(void *(*getProc)(const char *))\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\"); 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_MemAlloc = 0, 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"); + + for (int pass = PASS_MemAlloc; pass < PASS_LAST; pass++) { + if (pass == PASS_FunctionCall && !e->retval().isVoid()) { + fprintf(fp, "\t\t\t%s %s retval = ", e->retval().type()->name().c_str(), e->retval().isPointer() ? "*" : ""); + } + + + 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_MemAlloc) { + fprintf(fp, "\t\t\tsize_t tmpPtr%uSize = (size_t)*(unsigned int *)(ptr + %s);\n", + (uint) j, varoffset.c_str()); + fprintf(fp, "\t\t\tunsigned char *tmpPtr%u = new unsigned char[tmpPtr%uSize];\n", + (uint)j, (uint)j); + } 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_Epilog) { + // send back out pointers data; + for (size_t j = 0; j < evars.size(); j++) { + Var *v = &evars[j]; + if (v->isPointer() && (v->pointerDir() == Var::POINTER_OUT || v->pointerDir() == Var::POINTER_INOUT)) { + fprintf(fp, "\t\t\tstream->writeFully(tmpPtr%u, tmpPtr%uSize);\n", (uint) j, (uint) j); + if (v->pointerDir() == Var::POINTER_OUT) fprintf(fp, "delete tmpPtr%u;\n", (uint) j); + } + } + if (!e->retval().isVoid() && !e->retval().isPointer()) { + fprintf(fp, "\t\t\tstream->writeFully(&retval, sizeof(retval));\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"); + } + 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; +} + diff --git a/emulator/opengl/host/tools/emugen/ApiGen.h b/emulator/opengl/host/tools/emugen/ApiGen.h new file mode 100644 index 0000000..3c7fd27 --- /dev/null +++ b/emulator/opengl/host/tools/emugen/ApiGen.h @@ -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 diff --git a/emulator/opengl/host/tools/emugen/EntryPoint.cpp b/emulator/opengl/host/tools/emugen/EntryPoint.cpp new file mode 100644 index 0000000..d9b5499 --- /dev/null +++ b/emulator/opengl/host/tools/emugen/EntryPoint.cpp @@ -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; +} diff --git a/emulator/opengl/host/tools/emugen/EntryPoint.h b/emulator/opengl/host/tools/emugen/EntryPoint.h new file mode 100644 index 0000000..c417bda --- /dev/null +++ b/emulator/opengl/host/tools/emugen/EntryPoint.h @@ -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 diff --git a/emulator/opengl/host/tools/emugen/README b/emulator/opengl/host/tools/emugen/README new file mode 100644 index 0000000..18c3edb --- /dev/null +++ b/emulator/opengl/host/tools/emugen/README @@ -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’ pointer’s 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 + + diff --git a/emulator/opengl/host/tools/emugen/TypeFactory.cpp b/emulator/opengl/host/tools/emugen/TypeFactory.cpp new file mode 100644 index 0000000..709807e --- /dev/null +++ b/emulator/opengl/host/tools/emugen/TypeFactory.cpp @@ -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); +} + diff --git a/emulator/opengl/host/tools/emugen/TypeFactory.h b/emulator/opengl/host/tools/emugen/TypeFactory.h new file mode 100644 index 0000000..deee2ca --- /dev/null +++ b/emulator/opengl/host/tools/emugen/TypeFactory.h @@ -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 diff --git a/emulator/opengl/host/tools/emugen/Var.h b/emulator/opengl/host/tools/emugen/Var.h new file mode 100644 index 0000000..ef9f7c2 --- /dev/null +++ b/emulator/opengl/host/tools/emugen/Var.h @@ -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 diff --git a/emulator/opengl/host/tools/emugen/VarType.h b/emulator/opengl/host/tools/emugen/VarType.h new file mode 100644 index 0000000..a0718bb --- /dev/null +++ b/emulator/opengl/host/tools/emugen/VarType.h @@ -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 diff --git a/emulator/opengl/host/tools/emugen/errors.h b/emulator/opengl/host/tools/emugen/errors.h new file mode 100644 index 0000000..d09c292 --- /dev/null +++ b/emulator/opengl/host/tools/emugen/errors.h @@ -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 diff --git a/emulator/opengl/host/tools/emugen/main.cpp b/emulator/opengl/host/tools/emugen/main.cpp new file mode 100644 index 0000000..aefba0a --- /dev/null +++ b/emulator/opengl/host/tools/emugen/main.cpp @@ -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 + +} + diff --git a/emulator/opengl/host/tools/emugen/strUtils.cpp b/emulator/opengl/host/tools/emugen/strUtils.cpp new file mode 100644 index 0000000..357054b --- /dev/null +++ b/emulator/opengl/host/tools/emugen/strUtils.cpp @@ -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; +} + + diff --git a/emulator/opengl/host/tools/emugen/strUtils.h b/emulator/opengl/host/tools/emugen/strUtils.h new file mode 100644 index 0000000..3fa0908 --- /dev/null +++ b/emulator/opengl/host/tools/emugen/strUtils.h @@ -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 |