XFA: merge patch from CL 828183002, remove static declared SymbolDictCache
[pdfium.git] / core / src / fxcodec / jbig2 / JBig2_Context.cpp
index 543f9ab..bcd16e7 100644 (file)
@@ -4,7 +4,19 @@
  
 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
 
+#include <map>
+#include <list>
 #include "JBig2_Context.h"
+
+// Implement a very small least recently used (LRU) cache. It is very
+// common for a JBIG2 dictionary to span multiple pages in a PDF file,
+// and we do not want to decode the same dictionary over and over
+// again. We key off of the memory location of the dictionary. The
+// list keeps track of the freshness of entries, with freshest ones
+// at the front. Even a tiny cache size like 2 makes a dramatic
+// difference for typical JBIG2 documents.
+const int kSymbolDictCacheMaxSize = 2;
+
 void OutputBitmap(CJBig2_Image* pImage)
 {
     if(!pImage) {
@@ -12,9 +24,9 @@ void OutputBitmap(CJBig2_Image* pImage)
     }
 }
 CJBig2_Context *CJBig2_Context::CreateContext(CJBig2_Module *pModule, FX_BYTE *pGlobalData, FX_DWORD dwGlobalLength,
-        FX_BYTE *pData, FX_DWORD dwLength, FX_INT32 nStreamType, IFX_Pause* pPause)
+        FX_BYTE *pData, FX_DWORD dwLength, FX_INT32 nStreamType, std::list<CJBig2_CachePair>* pSymbolDictCache, IFX_Pause* pPause)
 {
-    return new(pModule) CJBig2_Context(pGlobalData, dwGlobalLength, pData, dwLength, nStreamType, pPause);
+    return new(pModule)CJBig2_Context(pGlobalData, dwGlobalLength, pData, dwLength, nStreamType, pSymbolDictCache, pPause);
 }
 void CJBig2_Context::DestroyContext(CJBig2_Context *pContext)
 {
@@ -23,11 +35,11 @@ void CJBig2_Context::DestroyContext(CJBig2_Context *pContext)
     }
 }
 CJBig2_Context::CJBig2_Context(FX_BYTE *pGlobalData, FX_DWORD dwGlobalLength,
-                               FX_BYTE *pData, FX_DWORD dwLength, FX_INT32 nStreamType, IFX_Pause* pPause)
+                               FX_BYTE *pData, FX_DWORD dwLength, FX_INT32 nStreamType, std::list<CJBig2_CachePair>* pSymbolDictCache, IFX_Pause* pPause)
 {
     if(pGlobalData && (dwGlobalLength > 0)) {
         JBIG2_ALLOC(m_pGlobalContext, CJBig2_Context(NULL, 0, pGlobalData, dwGlobalLength,
-                    JBIG2_EMBED_STREAM, pPause));
+                    JBIG2_EMBED_STREAM, pSymbolDictCache, pPause));
     } else {
         m_pGlobalContext = NULL;
     }
@@ -47,6 +59,7 @@ CJBig2_Context::CJBig2_Context(FX_BYTE *pGlobalData, FX_DWORD dwGlobalLength,
     m_pSegment = NULL;
     m_dwOffset = 0;
     m_ProcessiveStatus = FXCODEC_STATUS_FRAME_READY;
+    m_pSymbolDictCache = pSymbolDictCache;
 }
 CJBig2_Context::~CJBig2_Context()
 {
@@ -59,7 +72,7 @@ CJBig2_Context::~CJBig2_Context()
     }
     m_pGRD = NULL;
     if(m_gbContext) {
-        delete m_gbContext;
+        m_pModule->JBig2_Free(m_gbContext);
     }
     m_gbContext = NULL;
     if(m_pGlobalContext) {
@@ -614,6 +627,8 @@ 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;
+    FX_BOOL cache_hit = false;
     if(m_pStream->readShortInteger(&wFlags) != 0) {
         m_pModule->JBig2_Error("symbol dictionary segment : data header too short.");
         nRet = JBIG2_ERROR_TOO_SHORT;
@@ -791,23 +806,43 @@ FX_INT32 CJBig2_Context::parseSymbolDict(CJBig2_Segment *pSegment, IFX_Pause* pP
         }
     }
     pSegment->m_nResultType = JBIG2_SYMBOL_DICT_POINTER;
-    if(pSymbolDictDecoder->SDHUFF == 0) {
-        JBIG2_ALLOC(pArithDecoder, CJBig2_ArithDecoder(m_pStream));
-        pSegment->m_Result.sd = pSymbolDictDecoder->decode_Arith(pArithDecoder, gbContext, grContext);
-        delete pArithDecoder;
-        if(pSegment->m_Result.sd == NULL) {
-            nRet = JBIG2_ERROR_FETAL;
-            goto failed;
+    for(std::list<CJBig2_CachePair>::iterator it =
+            m_pSymbolDictCache->begin(); it != m_pSymbolDictCache->end(); ++it) {
+        if (it->first == key) {
+            pSegment->m_Result.sd = it->second->DeepCopy();
+            m_pSymbolDictCache->push_front(*it);
+            m_pSymbolDictCache->erase(it);
+            cache_hit = true;
+            break;
         }
-        m_pStream->alignByte();
-        m_pStream->offset(2);
-    } else {
-        pSegment->m_Result.sd = pSymbolDictDecoder->decode_Huffman(m_pStream, gbContext, grContext, pPause);
-        if(pSegment->m_Result.sd == NULL) {
-            nRet = JBIG2_ERROR_FETAL;
-            goto failed;
+    }
+    if (!cache_hit) {
+        if(pSymbolDictDecoder->SDHUFF == 0) {
+            JBIG2_ALLOC(pArithDecoder, CJBig2_ArithDecoder(m_pStream));
+            pSegment->m_Result.sd = pSymbolDictDecoder->decode_Arith(pArithDecoder, gbContext, grContext);
+            delete pArithDecoder;
+            if(pSegment->m_Result.sd == NULL) {
+                nRet = JBIG2_ERROR_FETAL;
+                goto failed;
+            }
+            m_pStream->alignByte();
+            m_pStream->offset(2);
+        } else {
+            pSegment->m_Result.sd = pSymbolDictDecoder->decode_Huffman(m_pStream, gbContext, grContext, pPause);
+            if(pSegment->m_Result.sd == NULL) {
+                nRet = JBIG2_ERROR_FETAL;
+                goto failed;
+            }
+            m_pStream->alignByte();
+        }
+        CJBig2_SymbolDict *value = pSegment->m_Result.sd->DeepCopy();
+        if (value && kSymbolDictCacheMaxSize > 0) {
+            while (m_pSymbolDictCache->size() >= kSymbolDictCacheMaxSize) {
+                delete m_pSymbolDictCache->back().second;
+                m_pSymbolDictCache->pop_back();
+            }
+            m_pSymbolDictCache->push_front(CJBig2_CachePair(key, value));
         }
-        m_pStream->alignByte();
     }
     if(wFlags & 0x0200) {
         pSegment->m_Result.sd->m_bContextRetained = TRUE;