add SkWriter32::rewindToOffset() -- used for peephole edits in picture recording



git-svn-id: http://skia.googlecode.com/svn/trunk@5345 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/include/core/SkWriter32.h b/include/core/SkWriter32.h
index 78cfb2b..f961736 100644
--- a/include/core/SkWriter32.h
+++ b/include/core/SkWriter32.h
@@ -49,6 +49,14 @@
      */
     void* getSingleBlock() const { return fSingleBlock; }
 
+    // return the current offset (will always be a multiple of 4)
+    uint32_t bytesWritten() const { return fSize; }
+    // DEPRECATED: use byetsWritten instead
+    uint32_t  size() const { return this->bytesWritten(); }
+
+    void      reset();
+    uint32_t* reserve(size_t size); // size MUST be multiple of 4
+    
     /**
      *  Specify the single block to back the writer, rathern than dynamically
      *  allocating the memory. If block == NULL, then the writer reverts to
@@ -152,16 +160,18 @@
      */
     static size_t WriteStringSize(const char* str, size_t len = (size_t)-1);
 
-    // return the current offset (will always be a multiple of 4)
-    uint32_t  size() const { return fSize; }
-    void      reset();
-    uint32_t* reserve(size_t size); // size MUST be multiple of 4
-
     // return the address of the 4byte int at the specified offset (which must
     // be a multiple of 4. This does not allocate any new space, so the returned
     // address is only valid for 1 int.
     uint32_t* peek32(size_t offset);
 
+    /**
+     *  Move the cursor back to offset bytes from the beginning.
+     *  This has the same restrictions as peek32: offset must be <= size() and
+     *  offset must be a multiple of 4.
+     */
+    void rewindToOffset(size_t offset);
+
     // copy into a single buffer (allocated by caller). Must be at least size()
     void flatten(void* dst) const;
 
@@ -185,6 +195,8 @@
     bool fHeadIsExternalStorage;
 
     Block* newBlock(size_t bytes);
+
+    SkDEBUGCODE(void validate() const;)
 };
 
 /**
diff --git a/src/core/SkWriter32.cpp b/src/core/SkWriter32.cpp
index 636af36..a94a92f 100644
--- a/src/core/SkWriter32.cpp
+++ b/src/core/SkWriter32.cpp
@@ -9,40 +9,40 @@
 
 struct SkWriter32::Block {
     Block*  fNext;
-    size_t  fSize;      // total space allocated (after this)
-    size_t  fAllocated; // space used so far
+    size_t  fSizeOfBlock;      // total space allocated (after this)
+    size_t  fAllocatedSoFar;    // space used so far
 
-    size_t  available() const { return fSize - fAllocated; }
+    size_t  available() const { return fSizeOfBlock - fAllocatedSoFar; }
     char*   base() { return (char*)(this + 1); }
     const char* base() const { return (const char*)(this + 1); }
 
     uint32_t* alloc(size_t size) {
         SkASSERT(SkAlign4(size) == size);
         SkASSERT(this->available() >= size);
-        void* ptr = this->base() + fAllocated;
-        fAllocated += size;
-        SkASSERT(fAllocated <= fSize);
+        void* ptr = this->base() + fAllocatedSoFar;
+        fAllocatedSoFar += size;
+        SkASSERT(fAllocatedSoFar <= fSizeOfBlock);
         return (uint32_t*)ptr;
     }
 
     uint32_t* peek32(size_t offset) {
-        SkASSERT(offset <= fAllocated + 4);
+        SkASSERT(offset <= fAllocatedSoFar + 4);
         void* ptr = this->base() + offset;
         return (uint32_t*)ptr;
     }
 
     void rewind() {
         fNext = NULL;
-        fAllocated = 0;
-        // keep fSize as is
+        fAllocatedSoFar = 0;
+        // keep fSizeOfBlock as is
     }
 
     static Block* Create(size_t size) {
         SkASSERT(SkAlign4(size) == size);
         Block* block = (Block*)sk_malloc_throw(sizeof(Block) + size);
         block->fNext = NULL;
-        block->fSize = size;
-        block->fAllocated = 0;
+        block->fSizeOfBlock = size;
+        block->fAllocatedSoFar = 0;
         return block;
     }
 
@@ -50,8 +50,8 @@
         SkASSERT(SkIsAlign4((intptr_t)storage));
         Block* block = (Block*)storage;
         block->fNext = NULL;
-        block->fSize = size - sizeof(Block);
-        block->fAllocated = 0;
+        block->fSizeOfBlock = size - sizeof(Block);
+        block->fAllocatedSoFar = 0;
         return block;
     }
 
@@ -140,6 +140,8 @@
 }
 
 uint32_t* SkWriter32::peek32(size_t offset) {
+    SkDEBUGCODE(this->validate();)
+    
     SkASSERT(SkAlign4(offset) == offset);
     SkASSERT(offset <= fSize);
 
@@ -150,14 +152,55 @@
     Block* block = fHead;
     SkASSERT(NULL != block);
 
-    while (offset >= block->fAllocated) {
-        offset -= block->fAllocated;
+    while (offset >= block->fAllocatedSoFar) {
+        offset -= block->fAllocatedSoFar;
         block = block->fNext;
         SkASSERT(NULL != block);
     }
     return block->peek32(offset);
 }
 
+void SkWriter32::rewindToOffset(size_t offset) {
+    if (0 == offset) {
+        this->reset(NULL, 0);
+        return;
+    }
+
+    SkDEBUGCODE(this->validate();)
+
+    SkASSERT(SkAlign4(offset) == offset);
+    SkASSERT(offset <= fSize);
+    fSize = offset;
+
+    if (fSingleBlock) {
+        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;
+        SkASSERT(NULL != block);
+    }
+
+    fTail = block;
+    block->fAllocatedSoFar = offset;
+    
+    // free up any following blocks
+    SkASSERT(block);
+    block = block->fNext;
+    while (block) {
+        Block* next = block->fNext;
+        sk_free(block);
+        block = next;
+    }
+
+    SkDEBUGCODE(this->validate();)
+}
+
 void SkWriter32::flatten(void* dst) const {
     if (fSingleBlock) {
         memcpy(dst, fSingleBlock, fSize);
@@ -168,7 +211,7 @@
     SkDEBUGCODE(size_t total = 0;)
 
     while (block) {
-        size_t allocated = block->fAllocated;
+        size_t allocated = block->fAllocatedSoFar;
         memcpy(dst, block->base(), allocated);
         dst = (char*)dst + allocated;
         block = block->fNext;
@@ -232,7 +275,7 @@
 
     const Block* block = fHead;
     while (block) {
-        if (!stream->write(block->base(), block->fAllocated)) {
+        if (!stream->write(block->base(), block->fAllocatedSoFar)) {
             return false;
         }
         block = block->fNext;
@@ -240,6 +283,30 @@
     return true;
 }
 
+#ifdef SK_DEBUG
+void SkWriter32::validate() const {
+    SkASSERT(SkIsAlign4(fSize));
+    SkASSERT(SkIsAlign4(fSingleBlockSize));
+
+    if (fSingleBlock) {
+        SkASSERT(fSize <= fSingleBlockSize);
+        return;
+    }
+
+    size_t accum = 0;
+    const Block* block = fHead;
+    while (block) {
+        SkASSERT(SkIsAlign4(block->fSizeOfBlock));
+        SkASSERT(SkIsAlign4(block->fAllocatedSoFar));
+        SkASSERT(block->fAllocatedSoFar <= block->fSizeOfBlock);
+        accum += block->fAllocatedSoFar;
+        SkASSERT(accum <= fSize);
+        block = block->fNext;
+    }
+    SkASSERT(accum == fSize);
+}
+#endif
+
 ///////////////////////////////////////////////////////////////////////////////
 
 #include "SkReader32.h"
diff --git a/tests/Writer32Test.cpp b/tests/Writer32Test.cpp
index f2dcf90..fdc0745 100644
--- a/tests/Writer32Test.cpp
+++ b/tests/Writer32Test.cpp
@@ -13,6 +13,32 @@
 #include "SkWriter32.h"
 #include "Test.h"
 
+static void check_contents(skiatest::Reporter* reporter, const SkWriter32& writer,
+                           const void* expected, size_t size) {
+    SkAutoSMalloc<256> storage(size);
+    REPORTER_ASSERT(reporter, writer.bytesWritten() == size);
+    writer.flatten(storage.get());
+    REPORTER_ASSERT(reporter, !memcmp(storage.get(), expected, size));
+}
+
+static void test_rewind(skiatest::Reporter* reporter) {
+    SkSWriter32<32> writer(32);
+    int32_t array[3] = { 1, 2, 4 };
+    
+    REPORTER_ASSERT(reporter, 0 == writer.bytesWritten());
+    for (size_t i = 0; i < SK_ARRAY_COUNT(array); ++i) {
+        writer.writeInt(array[i]);
+    }
+    check_contents(reporter, writer, array, sizeof(array));
+
+    writer.rewindToOffset(2*sizeof(int32_t));
+    REPORTER_ASSERT(reporter, sizeof(array) - 4 == writer.bytesWritten());
+    writer.writeInt(3);
+    REPORTER_ASSERT(reporter, sizeof(array) == writer.bytesWritten());
+    array[2] = 3;
+    check_contents(reporter, writer, array, sizeof(array));
+}
+
 static void test_ptr(skiatest::Reporter* reporter) {
     SkSWriter32<32> writer(32);
 
@@ -182,6 +208,7 @@
     }
 
     test_ptr(reporter);
+    test_rewind(reporter);
 }
 
 #include "TestClassDef.h"