Index: core/src/fxcodec/jbig2/JBig2_Context.cpp |
diff --git a/core/src/fxcodec/jbig2/JBig2_Context.cpp b/core/src/fxcodec/jbig2/JBig2_Context.cpp |
index 83ded6fbe18f15ac81bde0b7d0de95d9b25ad2be..865549a9fe452f99b9b0580c6f22b7f3fe835e2e 100644 |
--- a/core/src/fxcodec/jbig2/JBig2_Context.cpp |
+++ b/core/src/fxcodec/jbig2/JBig2_Context.cpp |
@@ -4,7 +4,22 @@ |
// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com |
+#include <map> |
+#include <vector> |
#include "JBig2_Context.h" |
+ |
+// Implement a very small least recently used (LRU) cache. It is very |
+// common for a JBIG2 dictionary to span multiple page in a PDF file, |
+// and we do not want to decode the same dictionary over an over |
+// again. We key off of the memory location of the dictionary. The |
+// vector keeps track of when an entry should be deleted, to make space |
+// for a new one. Even a tiny cache size like 2 makes a dramatic difference |
+// for Google Books PDF files. |
+const int kMaxCacheSize = 2; |
+static std::map<FX_BYTE*, CJBig2_SymbolDict*> cache; |
+static std::vector<FX_BYTE*> cache_keys; |
+ |
+ |
void OutputBitmap(CJBig2_Image* pImage) |
{ |
if(!pImage) { |
@@ -614,6 +629,7 @@ FX_INT32 CJBig2_Context::parseSymbolDict(CJBig2_Segment *pSegment, IFX_Pause* pP |
JBig2ArithCtx *gbContext = NULL, *grContext = NULL; |
CJBig2_ArithDecoder *pArithDecoder; |
JBIG2_ALLOC(pSymbolDictDecoder, CJBig2_SDDProc()); |
+ FX_BYTE *key = pSegment->m_pData; |
if(m_pStream->readShortInteger(&wFlags) != 0) { |
m_pModule->JBig2_Error("symbol dictionary segment : data header too short."); |
nRet = JBIG2_ERROR_TOO_SHORT; |
@@ -791,7 +807,17 @@ FX_INT32 CJBig2_Context::parseSymbolDict(CJBig2_Segment *pSegment, IFX_Pause* pP |
} |
} |
pSegment->m_nResultType = JBIG2_SYMBOL_DICT_POINTER; |
- if(pSymbolDictDecoder->SDHUFF == 0) { |
+ if (cache.find(key) != cache.end()) { |
+ pSegment->m_Result.sd = cache[key]->DeepCopy(); |
+ for(std::vector<FX_BYTE*>::iterator it = cache_keys.begin(); |
+ it != cache_keys.end(); ++it) { |
+ if (*it == key) { |
+ cache_keys.erase(cache_keys.begin()); |
Bo Xu
2014/12/06 05:59:43
Should line 815 be: "cache_keys.erase(it);" ? I gu
jab
2014/12/08 21:00:02
Done.
(I made the front hold the freshest entries
|
+ break; |
Bo Xu
2014/12/07 19:22:50
Or can we use std:list to replace the std:vector h
jab
2014/12/08 21:00:02
Done.
|
+ } |
+ } |
+ cache_keys.push_back(key); |
+ } else if(pSymbolDictDecoder->SDHUFF == 0) { |
JBIG2_ALLOC(pArithDecoder, CJBig2_ArithDecoder(m_pStream)); |
pSegment->m_Result.sd = pSymbolDictDecoder->decode_Arith(pArithDecoder, gbContext, grContext); |
delete pArithDecoder; |
@@ -809,6 +835,17 @@ FX_INT32 CJBig2_Context::parseSymbolDict(CJBig2_Segment *pSegment, IFX_Pause* pP |
} |
m_pStream->alignByte(); |
} |
+ if (cache.find(key) == cache.end()) { |
+ CJBig2_SymbolDict *value = pSegment->m_Result.sd->DeepCopy(); |
Bo Xu
2014/12/06 05:59:43
should |value| be deleted after use?
jab
2014/12/08 21:00:02
That you. I think I need to free the memory when w
Bo Xu
2014/12/08 23:00:17
How about making the cache member variable of the
jab
2014/12/08 23:21:04
I made an explicit call to delete. Is that okay? I
Bo Xu
2014/12/08 23:29:54
Sure, the explicit delete is ok here.
I am thinki
|
+ if (value) { |
+ while (cache_keys.size() >= kMaxCacheSize) { |
+ cache.erase(cache_keys[0]); |
+ cache_keys.erase(cache_keys.begin()); |
+ } |
+ cache.insert(std::pair<FX_BYTE*, CJBig2_SymbolDict*>(key, value)); |
+ cache_keys.push_back(key); |
+ } |
+ } |
if(wFlags & 0x0200) { |
pSegment->m_Result.sd->m_bContextRetained = TRUE; |
if(pSymbolDictDecoder->SDHUFF == 0) { |