speedup peek32() when the offset is in the last block (fTail)
Review URL: https://codereview.appspot.com/6906047

git-svn-id: http://skia.googlecode.com/svn/trunk@6708 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/include/core/SkWriter32.h b/include/core/SkWriter32.h
index 9354d6d..3b9bdfc 100644
--- a/include/core/SkWriter32.h
+++ b/include/core/SkWriter32.h
@@ -37,6 +37,7 @@
           fSize(0),
           fSingleBlock(NULL),
           fSingleBlockSize(0),
+          fWrittenBeforeLastBlock(0),
           fHead(NULL),
           fTail(NULL),
           fHeadIsExternalStorage(false) {}
@@ -197,6 +198,9 @@
     char*       fSingleBlock;
     uint32_t    fSingleBlockSize;
 
+    // sum of bytes written in all blocks *before* fTail
+    uint32_t    fWrittenBeforeLastBlock;
+
     struct Block;
     Block*  fHead;
     Block*  fTail;
diff --git a/src/core/SkWriter32.cpp b/src/core/SkWriter32.cpp
index 6a84927..ce240c2 100644
--- a/src/core/SkWriter32.cpp
+++ b/src/core/SkWriter32.cpp
@@ -66,6 +66,7 @@
     fSize = 0;
     fSingleBlock = NULL;
     fSingleBlockSize = 0;
+    fWrittenBeforeLastBlock = 0;
 
     storageSize &= ~3;  // trunc down to multiple of 4
     if (storageSize >= MIN_BLOCKSIZE) {
@@ -96,6 +97,7 @@
     }
 
     fSize = 0;
+    fWrittenBeforeLastBlock = 0;
     fSingleBlock = NULL;
     if (fHeadIsExternalStorage) {
         SkASSERT(fHead);
@@ -128,7 +130,11 @@
     if (NULL == block) {
         SkASSERT(NULL == fHead);
         fHead = fTail = block = Block::Create(SkMax32(size, fMinSize));
+        SkASSERT(0 == fWrittenBeforeLastBlock);
     } else if (block->available() < size) {
+        SkASSERT(fSize > 0);
+        fWrittenBeforeLastBlock = fSize;
+
         fTail = Block::Create(SkMax32(size, fMinSize));
         block->fNext = fTail;
         block = fTail;
@@ -149,6 +155,11 @@
         return (uint32_t*)(fSingleBlock + offset);
     }
 
+    // try the fast case, where offset is within fTail
+    if (offset >= fWrittenBeforeLastBlock) {
+        return fTail->peek32(offset - fWrittenBeforeLastBlock);
+    }
+    
     Block* block = fHead;
     SkASSERT(NULL != block);
 
@@ -179,29 +190,39 @@
         return;
     }
 
-    // Similar to peek32, except that we free up any following blocks
-    Block* block = fHead;
-    SkASSERT(NULL != block);
-    while (offset >= block->fAllocatedSoFar) {
-        offset -= block->fAllocatedSoFar;
-        block = block->fNext;
+    // Try the fast case, where offset is within fTail
+    if (offset >= fWrittenBeforeLastBlock) {
+        fTail->fAllocatedSoFar = offset - fWrittenBeforeLastBlock;
+    } else {
+        // Similar to peek32, except that we free up any following blocks.
+        // We have to re-compute fWrittenBeforeLastBlock as well.
+
+        size_t globalOffset = offset;
+        Block* block = fHead;
         SkASSERT(NULL != block);
-    }
+        while (offset >= block->fAllocatedSoFar) {
+            offset -= block->fAllocatedSoFar;
+            block = block->fNext;
+            SkASSERT(NULL != block);
+        }
 
-    // update the size on the "last" block
-    block->fAllocatedSoFar = offset;
-    // end our list
-    fTail = block;
-    Block* next = block->fNext;
-    block->fNext = NULL;
-    // free up any trailing blocks
-    block = next;
-    while (block) {
+        // this has to be recomputed, since we may free up fTail
+        fWrittenBeforeLastBlock = globalOffset - offset;
+
+        // update the size on the "last" block
+        block->fAllocatedSoFar = offset;
+        // end our list
+        fTail = block;
         Block* next = block->fNext;
-        sk_free(block);
+        block->fNext = NULL;
+        // free up any trailing blocks
         block = next;
+        while (block) {
+            Block* next = block->fNext;
+            sk_free(block);
+            block = next;
+        }
     }
-
     SkDEBUGCODE(this->validate();)
 }
 
@@ -310,6 +331,10 @@
         SkASSERT(SkIsAlign4(block->fSizeOfBlock));
         SkASSERT(SkIsAlign4(block->fAllocatedSoFar));
         SkASSERT(block->fAllocatedSoFar <= block->fSizeOfBlock);
+        if (NULL == block->fNext) {
+            SkASSERT(fTail == block);
+            SkASSERT(fWrittenBeforeLastBlock == accum);
+        }
         accum += block->fAllocatedSoFar;
         SkASSERT(accum <= fSize);
         block = block->fNext;