Index: poppler/Object.cc =================================================================== --- poppler/Object.cc (revision 55) +++ poppler/Object.cc (working copy) @@ -21,6 +21,8 @@ #include "UGooString.h" #include "XRef.h" +GooStringCache g_objectStringCache; + //------------------------------------------------------------------------ // Object //------------------------------------------------------------------------ @@ -76,10 +78,10 @@ *obj = *this; switch (type) { case objString: - obj->string = string->copy(); + obj->string = g_objectStringCache.alloc(string); break; case objName: - obj->name = copyString(name); + obj->name = g_objectStringCache.alloc(name); break; case objArray: array->incRef(); @@ -91,7 +93,7 @@ stream->incRef(); break; case objCmd: - obj->cmd = copyString(cmd); + obj->cmd = g_objectStringCache.alloc(cmd); break; default: break; @@ -107,13 +109,13 @@ xref->fetch(ref.num, ref.gen, obj) : copy(obj); } -void Object::free() { +void inline Object::free() { switch (type) { case objString: - delete string; + g_objectStringCache.free(string); break; case objName: - gfree(name); + g_objectStringCache.free(name); break; case objArray: if (!array->decRef()) { @@ -131,7 +133,7 @@ } break; case objCmd: - gfree(cmd); + g_objectStringCache.free(cmd); break; default: break; @@ -185,7 +187,9 @@ case objDict: fprintf(f, "<<"); for (i = 0; i < dictGetLength(); ++i) { - fprintf(f, " /%s ", dictGetKey(i)->getCString()); + char *key = dictGetKey(i)->getCStringCopy(); + fprintf(f, " /%s ", key); + delete[] key; dictGetValNF(i, &obj); obj.print(f); obj.free(); @@ -199,7 +203,7 @@ fprintf(f, "%d %d R", ref.num, ref.gen); break; case objCmd: - fprintf(f, "%s", cmd); + fprintf(f, "%s", cmd->getCString()); break; case objError: fprintf(f, ""); Index: poppler/GfxState.cc =================================================================== --- poppler/GfxState.cc (revision 55) +++ poppler/GfxState.cc (working copy) @@ -4157,7 +4157,7 @@ if (obj->isName()) { for (i = 0; i < nGfxBlendModeNames; ++i) { - if (!strcmp(obj->getName(), gfxBlendModeNames[i].name)) { + if (!strcmp(obj->getNameC(), gfxBlendModeNames[i].name)) { *mode = gfxBlendModeNames[i].mode; return gTrue; } @@ -4171,7 +4171,7 @@ return gFalse; } for (j = 0; j < nGfxBlendModeNames; ++j) { - if (!strcmp(obj2.getName(), gfxBlendModeNames[j].name)) { + if (!strcmp(obj2.getNameC(), gfxBlendModeNames[j].name)) { obj2.free(); *mode = gfxBlendModeNames[j].mode; return gTrue; Index: poppler/Link.cc =================================================================== --- poppler/Link.cc (revision 55) +++ poppler/Link.cc (working copy) @@ -90,7 +90,7 @@ // unknown action } else if (obj2.isName()) { - action = new LinkUnknown(obj2.getName()); + action = new LinkUnknown(obj2.getNameC()); // action is missing or wrong type } else { @@ -422,7 +422,7 @@ // named destination if (destObj->isName()) { - namedDest = new UGooString(destObj->getName()); + namedDest = new UGooString(destObj->getNameC()); } else if (destObj->isString()) { namedDest = new UGooString(*destObj->getString()); @@ -460,7 +460,7 @@ // named destination if (destObj->isName()) { - namedDest = new UGooString(destObj->getName()); + namedDest = new UGooString(destObj->getNameC()); } else if (destObj->isString()) { namedDest = new UGooString(*destObj->getString()); Index: poppler/UGooString.h =================================================================== --- poppler/UGooString.h (revision 55) +++ poppler/UGooString.h (working copy) @@ -54,7 +54,7 @@ // Return a newly allocated copy of the string converted to // ascii (non-Unicode) format. Caller has to delete [] the result - char *getCString() const; + char *getCStringCopy() const; // a special value telling that the length of the string is not given // so it must be calculated from the strings Index: poppler/SecurityHandler.cc =================================================================== --- poppler/SecurityHandler.cc (revision 55) +++ poppler/SecurityHandler.cc (working copy) @@ -147,8 +147,8 @@ if (cryptFiltersObj.isDict() && streamFilterObj.isName() && stringFilterObj.isName() && - !strcmp(streamFilterObj.getName(), stringFilterObj.getName())) { - if (cryptFiltersObj.dictLookup(streamFilterObj.getName(), + !strcmp(streamFilterObj.getNameC(), stringFilterObj.getNameC())) { + if (cryptFiltersObj.dictLookup(streamFilterObj.getNameC(), &cryptFilterObj)->isDict()) { if (cryptFilterObj.dictLookup("CFM", &cfmObj)->isName("V2")) { encVersion = 2; Index: poppler/Object.h =================================================================== --- poppler/Object.h (revision 55) +++ poppler/Object.h (working copy) @@ -25,6 +25,8 @@ class Dict; class Stream; +extern GooStringCache g_objectStringCache; + //------------------------------------------------------------------------ // Ref //------------------------------------------------------------------------ @@ -86,10 +88,16 @@ { initObj(objInt); intg = intgA; return this; } Object *initReal(double realA) { initObj(objReal); real = realA; return this; } - Object *initString(GooString *stringA) - { initObj(objString); string = stringA; return this; } - Object *initName(char *nameA) - { initObj(objName); name = copyString(nameA); return this; } + Object *initString(GooString *stringA) { + initObj(objString); + string = stringA; + return this; } + Object *initName(char *nameA) { + initObj(objName); + name = g_objectStringCache.alloc(nameA); + return this; + } + Object *initNull() { initObj(objNull); return this; } Object *initArray(XRef *xref); @@ -98,8 +106,11 @@ Object *initStream(Stream *streamA); Object *initRef(int numA, int genA) { initObj(objRef); ref.num = numA; ref.gen = genA; return this; } - Object *initCmd(char *cmdA) - { initObj(objCmd); cmd = copyString(cmdA); return this; } + Object *initCmd(char *cmdA) { + initObj(objCmd); + cmd = g_objectStringCache.alloc(cmdA); + return this; + } Object *initError() { initObj(objError); return this; } Object *initEOF() @@ -139,11 +150,11 @@ // Special type checking. GBool isName(char *nameA) - { return type == objName && !strcmp(name, nameA); } + { return type == objName && (0 == name->cmp(nameA)); } GBool isDict(char *dictType); GBool isStream(char *dictType); GBool isCmd(char *cmdA) - { return type == objCmd && !strcmp(cmd, cmdA); } + { return type == objCmd && (0 == cmd->cmp(cmdA)); } // Accessors. NB: these assume object is of correct type. GBool getBool() { return booln; } @@ -151,14 +162,15 @@ double getReal() { return real; } double getNum() { return type == objInt ? (double)intg : real; } GooString *getString() { return string; } - char *getName() { return name; } + GooString *getName() { return name; } + char *getNameC() { return name->getCString(); } Array *getArray() { return array; } Dict *getDict() { return dict; } Stream *getStream() { return stream; } Ref getRef() { return ref; } int getRefNum() { return ref.num; } int getRefGen() { return ref.gen; } - char *getCmd() { return cmd; } + GooString *getCmd() { return cmd; } // Array accessors. int arrayGetLength(); @@ -205,23 +217,23 @@ private: - ObjType type; // object type - union { // value for each type: - GBool booln; // boolean - int intg; // integer - double real; // real - GooString *string; // string - char *name; // name - Array *array; // array - Dict *dict; // dictionary - Stream *stream; // stream - Ref ref; // indirect reference - char *cmd; // command + ObjType type; // object type + union { // value for each type: + GBool booln; // boolean + int intg; // integer + double real; // real + GooString *string; // string + GooString *name; // name + Array *array; // array + Dict *dict; // dictionary + Stream *stream; // stream + Ref ref; // indirect reference + GooString *cmd; // command }; #ifdef DEBUG_MEM - static int // number of each type of object - numAlloc[numObjTypes]; // currently allocated + static int // number of each type of object + numAlloc[numObjTypes]; // currently allocated #endif }; Index: poppler/Stream.cc =================================================================== --- poppler/Stream.cc (revision 55) +++ poppler/Stream.cc (working copy) @@ -117,7 +117,7 @@ dict->dictLookup("DP", ¶ms); } if (obj.isName()) { - str = makeFilter(obj.getName(), str, ¶ms); + str = makeFilter(obj.getNameC(), str, ¶ms); } else if (obj.isArray()) { for (i = 0; i < obj.arrayGetLength(); ++i) { obj.arrayGet(i, &obj2); @@ -126,7 +126,7 @@ else params2.initNull(); if (obj2.isName()) { - str = makeFilter(obj2.getName(), str, ¶ms2); + str = makeFilter(obj2.getNameC(), str, ¶ms2); } else { error(getPos(), "Bad filter name"); str = new EOFStream(str); Index: poppler/Parser.cc =================================================================== --- poppler/Parser.cc (revision 55) +++ poppler/Parser.cc (working copy) @@ -76,7 +76,7 @@ shift(); } else { // buf1 might go away in shift(), so construct the key - UGooString *key = new UGooString(buf1.getName()); + UGooString *key = new UGooString(buf1.getNameC()); shift(); if (buf1.isEOF() || buf1.isError()) { break; Index: poppler/UGooString.cc =================================================================== --- poppler/UGooString.cc (revision 55) +++ poppler/UGooString.cc (working copy) @@ -29,43 +29,45 @@ // We assume that if this is being called from the constructor, was set // to NULL and was set to 0 to indicate unused string before calling us. void inline UGooString::resize(int newLength) { - Unicode *s1 = s; + int curSize = roundedSize(length); + int newSize = roundedSize(newLength); - if (!s || (roundedSize(length) != roundedSize(newLength))) { - // requires re-allocating data for string - if (newLength < STR_STATIC_SIZE) - s1 = sStatic; - else - s1 = new Unicode[roundedSize(newLength)]; + assert(s); + if (curSize != newSize) { + Unicode *sNew = sStatic; + if (newSize != STR_STATIC_SIZE) + sNew = new Unicode[newSize]; + // we had to re-allocate the memory, so copy the content of previous // buffer into a new buffer - if (s) { - if (newLength < length) { - memcpy(s1, s, newLength); - } else { - memcpy(s1, s, length); - } + if (newLength < length) { + memcpy(sNew, s, newLength * sizeof(Unicode)); + } else { + memcpy(sNew, s, length * sizeof(Unicode)); } - if (s != sStatic) + + if (s != sStatic) { + assert(curSize != STR_STATIC_SIZE); delete[] s; + } + s = sNew; } - s = s1; length = newLength; s[length] = '\0'; } UGooString::UGooString(void) { - s = NULL; + s = sStatic; length = 0; resize(0); } UGooString::UGooString(GooString &str) { - s = NULL; + s = sStatic; length = 0; if (str.hasUnicodeMarker()) { @@ -79,14 +81,14 @@ UGooString::UGooString(const UGooString &str) { - s = NULL; + s = sStatic; length = 0; Set(str); } UGooString::UGooString(const char *str, int strLen) { - s = NULL; + s = sStatic; length = 0; if (CALC_STRING_LEN == strLen) strLen = strlen(str); @@ -116,7 +118,7 @@ break; } } - if ( foundUnencoded ) + if (foundUnencoded) { for (j = 0; j < length; ++j) { s[j] = str[j]; @@ -187,15 +189,7 @@ return n1 - n2; } -// FIXME: -// a) this is confusing because GooString::getCString() returns a pointer -// but UGooString returns a newly allocated copy. Should give this -// a different name, like copyAsAscii() or copyAsGooString() -// b) this interface requires copying. It should be changed to take a -// GooString& as a param and put the data inside it so that it uses -// caching optimization of GooString. Callers should be changed to use -// this new interface -char *UGooString::getCString() const +char *UGooString::getCStringCopy() const { char *res = new char[length + 1]; for (int i = 0; i < length; i++) res[i] = s[i]; Index: poppler/Catalog.cc =================================================================== --- poppler/Catalog.cc (revision 55) +++ poppler/Catalog.cc (working copy) @@ -205,7 +205,7 @@ dict = metadata.streamGetDict(); if (!dict->lookup("Subtype", &obj)->isName("XML")) { error(-1, "Unknown Metadata type: '%s'", - obj.isName() ? obj.getName() : "???"); + obj.isName() ? obj.getNameC() : "???"); } obj.free(); s = new GooString(); @@ -351,7 +351,7 @@ Object obj, obj2; obj = embeddedFileNameTree.getValue(i); GooString *fileName = new GooString(); - char *descString = embeddedFileNameTree.getName(i)->getCString(); + char *descString = embeddedFileNameTree.getName(i)->getCStringCopy(); GooString *desc = new GooString(descString); delete[] descString; GooString *createDate = new GooString(); @@ -527,7 +527,7 @@ (*entry)->value.fetch(xref, obj); return gTrue; } else { - cname = name->getCString(); + cname = name->getCStringCopy(); printf("failed to look up %s\n", cname); delete[] cname; obj->initNull(); Index: poppler/GfxFont.cc =================================================================== --- poppler/GfxFont.cc (revision 55) +++ poppler/GfxFont.cc (working copy) @@ -129,7 +129,7 @@ font = new GfxCIDFont(xref, tagA, idA, nameA, fontDict); } else { error(-1, "Unknown font type: '%s'", - obj1.isName() ? obj1.getName() : "???"); + obj1.isName() ? obj1.getNameC() : "???"); font = new Gfx8BitFont(xref, tagA, idA, nameA, fontUnknownType, fontDict); } obj1.free(); @@ -212,15 +212,16 @@ // get stretch obj1.dictLookup("FontStretch", &obj2); if (obj2.isName()) { - if (strcmp(obj2.getName(), "UltraCondensed") == 0) stretch = UltraCondensed; - else if (strcmp(obj2.getName(), "ExtraCondensed") == 0) stretch = ExtraCondensed; - else if (strcmp(obj2.getName(), "Condensed") == 0) stretch = Condensed; - else if (strcmp(obj2.getName(), "SemiCondensed") == 0) stretch = SemiCondensed; - else if (strcmp(obj2.getName(), "Normal") == 0) stretch = Normal; - else if (strcmp(obj2.getName(), "SemiExpanded") == 0) stretch = SemiExpanded; - else if (strcmp(obj2.getName(), "Expanded") == 0) stretch = Expanded; - else if (strcmp(obj2.getName(), "ExtraExpanded") == 0) stretch = ExtraExpanded; - else if (strcmp(obj2.getName(), "UltraExpanded") == 0) stretch = UltraExpanded; + char *name = obj2.getNameC(); + if (strcmp(name, "UltraCondensed") == 0) stretch = UltraCondensed; + else if (strcmp(name, "ExtraCondensed") == 0) stretch = ExtraCondensed; + else if (strcmp(name, "Condensed") == 0) stretch = Condensed; + else if (strcmp(name, "SemiCondensed") == 0) stretch = SemiCondensed; + else if (strcmp(name, "Normal") == 0) stretch = Normal; + else if (strcmp(name, "SemiExpanded") == 0) stretch = SemiExpanded; + else if (strcmp(name, "Expanded") == 0) stretch = Expanded; + else if (strcmp(name, "ExtraExpanded") == 0) stretch = ExtraExpanded; + else if (strcmp(name, "UltraExpanded") == 0) stretch = UltraExpanded; else error(-1, "Invalid Font Stretch"); } obj2.free(); @@ -289,7 +290,7 @@ type = fontCIDType0C; } else { error(-1, "Unknown embedded font type '%s'", - obj4.isName() ? obj4.getName() : "???"); + obj4.isName() ? obj4.getNameC() : "???"); } obj4.free(); } @@ -719,7 +720,7 @@ if (encFree[code]) { gfree(enc[code]); } - enc[code] = copyString(obj3.getName()); + enc[code] = copyString(obj3.getNameC()); encFree[code] = gTrue; } ++code; @@ -1159,7 +1160,7 @@ type = fontCIDType2; } else { error(-1, "Unknown Type 0 descendant font type '%s'", - obj1.isName() ? obj1.getName() : "???"); + obj1.isName() ? obj1.getNameC() : "???"); goto err3; } obj1.free(); @@ -1608,7 +1609,7 @@ r.gen = 999999; } } - char *aux = fontDict->getKey(i)->getCString(); + char *aux = fontDict->getKey(i)->getCStringCopy(); fonts[i] = GfxFont::makeFont(xref, aux, r, obj2.getDict()); delete[] aux; Index: poppler/Gfx.cc =================================================================== --- poppler/Gfx.cc (revision 55) +++ poppler/Gfx.cc (working copy) @@ -673,7 +673,7 @@ int i; // find operator - name = cmd->getCmd(); + name = cmd->getCmd()->getCString(); if (!(op = findOp(name))) { if (ignoreUndef == 0) error(getPos(), "Unknown operator '%s'", name); @@ -827,7 +827,7 @@ GfxBlendMode mode; GBool haveFillOP; - if (!res->lookupGState(args[0].getName(), &obj1)) { + if (!res->lookupGState(args[0].getNameC(), &obj1)) { return; } if (!obj1.isDict()) { @@ -968,7 +968,7 @@ int i; state->setFillPattern(NULL); - res->lookupColorSpace(args[0].getName(), &obj); + res->lookupColorSpace(args[0].getNameC(), &obj); if (obj.isNull()) { colorSpace = GfxColorSpace::parse(&args[0]); } else { @@ -995,7 +995,7 @@ int i; state->setStrokePattern(NULL); - res->lookupColorSpace(args[0].getName(), &obj); + res->lookupColorSpace(args[0].getNameC(), &obj); if (obj.isNull()) { colorSpace = GfxColorSpace::parse(&args[0]); } else { @@ -1055,7 +1055,7 @@ out->updateFillColor(state); } if (args[numArgs-1].isName() && - (pattern = res->lookupPattern(args[numArgs-1].getName()))) { + (pattern = res->lookupPattern(args[numArgs-1].getNameC()))) { state->setFillPattern(pattern); } @@ -1087,7 +1087,7 @@ out->updateStrokeColor(state); } if (args[numArgs-1].isName() && - (pattern = res->lookupPattern(args[numArgs-1].getName()))) { + (pattern = res->lookupPattern(args[numArgs-1].getNameC()))) { state->setStrokePattern(pattern); } @@ -1622,7 +1622,7 @@ GfxPath *savedPath; double xMin, yMin, xMax, yMax; - if (!(shading = res->lookupShading(args[0].getName()))) { + if (!(shading = res->lookupShading(args[0].getNameC()))) { return; } @@ -2514,7 +2514,7 @@ void Gfx::opSetFont(Object args[], int numArgs) { GfxFont *font; - if (!(font = res->lookupFont(args[0].getName()))) { + if (!(font = res->lookupFont(args[0].getNameC()))) { return; } if (printCommands) { @@ -2882,11 +2882,11 @@ Object opiDict; #endif - if (!res->lookupXObject(args[0].getName(), &obj1)) { + if (!res->lookupXObject(args[0].getNameC(), &obj1)) { return; } if (!obj1.isStream()) { - error(getPos(), "XObject '%s' is wrong type", args[0].getName()); + error(getPos(), "XObject '%s' is wrong type", args[0].getNameC()); obj1.free(); return; } @@ -2899,7 +2899,7 @@ obj1.streamGetDict()->lookup("Subtype", &obj2); if (obj2.isName("Image")) { if (out->needNonText()) { - res->lookupXObjectNF(args[0].getName(), &refObj); + res->lookupXObjectNF(args[0].getNameC(), &refObj); doImage(&refObj, obj1.getStream(), gFalse); refObj.free(); } @@ -3034,7 +3034,7 @@ dict->lookup("CS", &obj1); } if (obj1.isName()) { - res->lookupColorSpace(obj1.getName(), &obj2); + res->lookupColorSpace(obj1.getNameC(), &obj2); if (!obj2.isNull()) { obj1.free(); obj1 = obj2; @@ -3120,7 +3120,7 @@ maskDict->lookup("CS", &obj1); } if (obj1.isName()) { - res->lookupColorSpace(obj1.getName(), &obj2); + res->lookupColorSpace(obj1.getNameC(), &obj2); if (!obj2.isNull()) { obj1.free(); obj1 = obj2; @@ -3511,7 +3511,6 @@ Object dict; Object obj; Object dictObj; - char *key; Stream *str; GBool isEOF = gFalse; @@ -3534,8 +3533,7 @@ dictObj.free(); break; } - key = obj.getName(); - dict.dictAdd(key, &dictObj); + dict.dictAddOwnVal(obj.getNameC(), &dictObj); } obj.free(); } @@ -3602,9 +3600,9 @@ } if(numArgs == 2) { - out->beginMarkedContent(args[0].getName(),args[1].getDict()); + out->beginMarkedContent(args[0].getNameC(),args[1].getDict()); } else { - out->beginMarkedContent(args[0].getName()); + out->beginMarkedContent(args[0].getNameC()); } } @@ -3622,9 +3620,9 @@ } if(numArgs == 2) { - out->markPoint(args[0].getName(),args[1].getDict()); + out->markPoint(args[0].getNameC(),args[1].getDict()); } else { - out->markPoint(args[0].getName()); + out->markPoint(args[0].getNameC()); } } Index: poppler/Annot.cc =================================================================== --- poppler/Annot.cc (revision 55) +++ poppler/Annot.cc (working copy) @@ -84,7 +84,7 @@ if (dict->lookup("AP", &apObj)->isDict()) { if (dict->lookup("AS", &asObj)->isName()) { if (apObj.dictLookup("N", &obj1)->isDict()) { - if (obj1.dictLookupNF(asObj.getName(), &obj2)->isRef()) { + if (obj1.dictLookupNF(asObj.getNameC(), &obj2)->isRef()) { obj2.copy(&appearance); ok = gTrue; } else { Index: goo/GooString.cc =================================================================== --- goo/GooString.cc (revision 55) +++ goo/GooString.cc (working copy) @@ -35,29 +35,30 @@ // We assume that if this is being called from the constructor, was set // to NULL and was set to 0 to indicate unused string before calling us. void inline GooString::resize(int newLength) { - char *s1 = s; + int curSize = roundedSize(length); + int newSize = roundedSize(newLength); - if (!s || (roundedSize(length) != roundedSize(newLength))) { - // requires re-allocating data for string - if (newLength < STR_STATIC_SIZE) - s1 = sStatic; - else - s1 = new char[roundedSize(newLength)]; + assert(s); + if (curSize != newSize) { + char *sNew = sStatic; + if (newSize != STR_STATIC_SIZE) + sNew = new char[newSize]; + // we had to re-allocate the memory, so copy the content of previous // buffer into a new buffer - if (s) { - if (newLength < length) { - memcpy(s1, s, newLength); - } else { - memcpy(s1, s, length); - } + if (newLength < length) { + memcpy(sNew, s, newLength); + } else { + memcpy(sNew, s, length); } - if (s != sStatic) + if (s != sStatic) { + assert(curSize != STR_STATIC_SIZE); delete[] s; + } + s = sNew; } - s = s1; length = newLength; s[length] = '\0'; } @@ -97,38 +98,38 @@ } GooString::GooString() { - s = NULL; + s = sStatic; length = 0; Set(NULL); } GooString::GooString(const char *sA) { - s = NULL; + s = sStatic; length = 0; Set(sA, CALC_STRING_LEN); } GooString::GooString(const char *sA, int lengthA) { - s = NULL; + s = sStatic; length = 0; Set(sA, lengthA); } GooString::GooString(GooString *str, int idx, int lengthA) { - s = NULL; + s = sStatic; length = 0; assert(idx + lengthA < str->length); Set(str->getCString() + idx, lengthA); } GooString::GooString(GooString *str) { - s = NULL; + s = sStatic; length = 0; Set(str->getCString(), str->length); } GooString::GooString(GooString *str1, GooString *str2) { - s = NULL; + s = sStatic; length = 0; Set(str1->getCString(), str1->length, str2->getCString(), str2->length); } Index: goo/GooString.h =================================================================== --- goo/GooString.h (revision 55) +++ goo/GooString.h (working copy) @@ -40,8 +40,9 @@ // length of string (or its substring) GooString* GooString::Set(const char *s1, int s1Len=CALC_STRING_LEN, const char *s2=NULL, int s2Len=CALC_STRING_LEN); - // Copy a string. GooString(GooString *str); + + // Return a newly allocated copy of the string GooString *copy() { return new GooString(this); } // Concatenate two strings. @@ -50,14 +51,13 @@ // Convert an integer to a string. static GooString *fromInt(int x); - // Destructor. ~GooString(); // Get length. - int getLength() { return length; } + int getLength() const { return length; } // Get C string. - char *getCString() { return s; } + char *getCString() const { return s; } // Get th character. char getChar(int i) { return s[i]; } @@ -93,15 +93,16 @@ GBool hasUnicodeMarker(void); + // a special value telling that the length of the string is not given + // so it must be calculated from the strings + static const int CALC_STRING_LEN = -1; + private: // you can tweak this number for a different speed/memory usage tradeoffs. // In libc malloc() rounding is 16 so it's best to choose a value that // results in sizeof(GooString) be a multiple of 16. // 24 makes sizeof(GooString) to be 32. static const int STR_STATIC_SIZE = 24; - // a special value telling that the length of the string is not given - // so it must be calculated from the strings - static const int CALC_STRING_LEN = -1; int roundedSize(int len); @@ -112,4 +113,87 @@ void resize(int newLength); }; +//Uncomment if you want to gather stats on hit rate of the cache +//#define CALC_OBJECT_STRING_CACHE_STATS 1 + +/* A cache for GooString. You can think of it as a custom allocator + for GooString(). Use alloc() to get a new GooString() and free() to free + existing GooString(). It keeps last GooStringCache::CACHE_SIZE free()ed + strings in a cache (which is a stack) and satisfies the alloc()s from + the cache first, thus saves free()/malloc() cycle. + It's used by Object::free()/Object::init*() and works great for them + because they recycle strings like crazy. +*/ +class GooStringCache +{ +public: + GooStringCache() { + inCache = 0; +#ifdef CALC_OBJECT_STRING_CACHE_STATS + totalAllocs = 0; + allocsFromCache = 0; #endif + } + + ~GooStringCache() { + for (int i=0; igetCString(), str->getLength()); + } + + // alloc and free are called a lot, so make them inline + GooString *alloc(const char *txt, int strLen = GooString::CALC_STRING_LEN) { +#ifdef CALC_OBJECT_STRING_CACHE_STATS + ++totalAllocs; +#endif + if (inCache > 0) { + GooString *res; + // pop the value from the top of the stack + res = stringsCached[inCache-1]; + res->Set(txt, strLen); + --inCache; +#ifdef CALC_OBJECT_STRING_CACHE_STATS + ++allocsFromCache; +#endif + return res; + } else { + return new GooString(txt); + } + } + + void free(GooString *str) { + if (inCache < CACHE_SIZE) { + // put the value at the top of the stack + stringsCached[inCache] = str; + ++inCache; + } else { + // cache is full + delete str; + } + } +private: + // CACHE_SIZE size affects 2 things: + // - alloc() hit ratio i.e. how many alloc()s can be satisfied from cache + // as opposed to allocating new GooString() with generic malloc() + // This is a *very* effective cache. I get 99.98% alloc() hit ratio even + // with CACHE_SIZE of 8. 95% with CACHE_SIZE of 4 + // - how often we call delete on GooString(). When cache is full, we delete + // strings the usual way. When CACHE_SIZE grows, we hit delete less + // 64 is chosen by gut feeling, might use some tweaking + static const int CACHE_SIZE = 64; + +#ifdef CALC_OBJECT_STRING_CACHE_STATS + int totalAllocs; + int allocsFromCache; +#endif + int inCache; + // you can think of it as a stack, we only add something to the top + // or take it from the top + GooString *stringsCached[CACHE_SIZE]; +}; + +#endif