Avoid a loop in writeString and writePad by zeroing padding first.

Also add a benchmark to time the new improved writeString. Before
my change the bench took ~1.23ms and afterwards it takes ~.95ms.

Add some testing to ensure that writePad works properly.

TEST=Writer32Test, WriterBench

Review URL: https://codereview.appspot.com/6438045

git-svn-id: http://skia.googlecode.com/svn/trunk@4742 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/bench/WriterBench.cpp b/bench/WriterBench.cpp
new file mode 100644
index 0000000..2de653c
--- /dev/null
+++ b/bench/WriterBench.cpp
@@ -0,0 +1,40 @@
+
+/*
+ * Copyright 2012 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkBenchmark.h"
+#include "SkCanvas.h"
+#include "SkWriter32.h"
+
+class WriterBench : public SkBenchmark {
+public:
+    WriterBench(void* param) : INHERITED(param) {}
+
+protected:
+    virtual const char* onGetName() SK_OVERRIDE {
+        return "writer";
+    }
+
+    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+        static const char gStr[] = "abcdefghimjklmnopqrstuvwxyz";
+        static const size_t gLen = strlen(gStr);
+        SkWriter32 writer(256 * 4);
+        for (int i = 0; i < SkBENCHLOOP(800); i++) {
+            for (size_t j = 0; j <= gLen; j++) {
+                writer.writeString(gStr, j);
+            }
+        }
+    }
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+static SkBenchmark* fact(void* p) { return new WriterBench(p); }
+static BenchRegistry gReg(fact);
diff --git a/gyp/bench.gypi b/gyp/bench.gypi
index f809b64..e32db41 100644
--- a/gyp/bench.gypi
+++ b/gyp/bench.gypi
@@ -34,6 +34,7 @@
     '../bench/ShaderMaskBench.cpp',
     '../bench/TextBench.cpp',
     '../bench/VertBench.cpp',
+    '../bench/WriterBench.cpp',
   ],
 }
 
diff --git a/src/core/SkWriter32.cpp b/src/core/SkWriter32.cpp
index 23f51b9..926a430 100644
--- a/src/core/SkWriter32.cpp
+++ b/src/core/SkWriter32.cpp
@@ -180,13 +180,15 @@
 }
 
 void SkWriter32::writePad(const void* src, size_t size) {
-    size_t alignedSize = SkAlign4(size);
-    char* dst = (char*)this->reserve(alignedSize);
-    memcpy(dst, src, size);
-    dst += size;
-    int n = alignedSize - size;
-    while (--n >= 0) {
-        *dst++ = 0;
+    if (size > 0) {
+        size_t alignedSize = SkAlign4(size);
+        char* dst = (char*)this->reserve(alignedSize);
+        // Pad the last four bytes with zeroes in one step. Some (or all) will
+        // be overwritten by the memcpy.
+        uint32_t* padding = (uint32_t*)(dst + (alignedSize - 4));
+        *padding = 0;
+        // Copy the actual data.
+        memcpy(dst, src, size);
     }
 }
 
@@ -279,13 +281,13 @@
     // add 1 since we also write a terminating 0
     size_t alignedLen = SkAlign4(len + 1);
     char* ptr = (char*)this->reserve(alignedLen);
-    memcpy(ptr, str, len);
-    // Add the terminating 0, and pad the rest with 0s
-    ptr += len;
-    int n = alignedLen - len;
-    while (--n >= 0) {
-        *ptr++ = 0;
+    {
+        // Write the terminating 0 and fill in the rest with zeroes
+        uint32_t* padding = (uint32_t*)(ptr + (alignedLen - 4));
+        *padding = 0;
     }
+    // Copy the string itself.
+    memcpy(ptr, str, len);
 }
 
 size_t SkWriter32::WriteStringSize(const char* str, size_t len) {
diff --git a/tests/Writer32Test.cpp b/tests/Writer32Test.cpp
index 5c9d7ea..8b048a7 100644
--- a/tests/Writer32Test.cpp
+++ b/tests/Writer32Test.cpp
@@ -8,6 +8,7 @@
 
 
 
+#include "SkRandom.h"
 #include "SkReader32.h"
 #include "SkWriter32.h"
 #include "Test.h"
@@ -86,6 +87,48 @@
     REPORTER_ASSERT(reporter, reader.eof());
 }
 
+static void testWritePad(skiatest::Reporter* reporter, SkWriter32* writer) {
+    // Create some random data to write.
+    const size_t dataSize = 10<<2;
+    SkASSERT(SkIsAlign4(dataSize));
+
+    SkAutoMalloc originalData(dataSize);
+    {
+        SkRandom rand(0);
+        uint32_t* ptr = static_cast<uint32_t*>(originalData.get());
+        uint32_t* stop = ptr + (dataSize>>2);
+        while (ptr < stop) {
+            *ptr++ = rand.nextU();
+        }
+
+        // Write  the random data to the writer at different lengths for
+        // different alignments.
+        for (size_t len = 0; len < dataSize; len++) {
+            writer->writePad(originalData.get(), len);
+        }
+    }
+
+    uint32_t totalBytes = writer->size();
+
+    SkAutoMalloc readStorage(totalBytes);
+    writer->flatten(readStorage.get());
+
+    SkReader32 reader;
+    reader.setMemory(readStorage.get(), totalBytes);
+
+    for (size_t len = 0; len < dataSize; len++) {
+        const char* readPtr = static_cast<const char*>(reader.skip(len));
+        // Ensure that the data read is the same as what was written.
+        REPORTER_ASSERT(reporter, memcmp(readPtr, originalData.get(), len) == 0);
+        // Ensure that the rest is padded with zeroes.
+        const char* stop = readPtr + SkAlign4(len);
+        readPtr += len;
+        while (readPtr < stop) {
+            REPORTER_ASSERT(reporter, *readPtr++ == 0);
+        }
+    }
+}
+
 static void Tests(skiatest::Reporter* reporter) {
     // dynamic allocator
     {
@@ -95,6 +138,9 @@
         
         writer.reset();
         test2(reporter, &writer);
+
+        writer.reset();
+        testWritePad(reporter, &writer);
     }
     
     // single-block
@@ -108,6 +154,9 @@
 
         writer.reset(storage, sizeof(storage));
         test2(reporter, &writer);
+        
+        writer.reset(storage, sizeof(storage));
+        testWritePad(reporter, &writer);
     }
     
     // small storage
@@ -116,6 +165,9 @@
         test1(reporter, &writer);
         writer.reset(); // should just rewind our storage
         test2(reporter, &writer);
+        
+        writer.reset();
+        testWritePad(reporter, &writer);
     }
     
     // large storage
@@ -124,6 +176,9 @@
         test1(reporter, &writer);
         writer.reset(); // should just rewind our storage
         test2(reporter, &writer);
+        
+        writer.reset();
+        testWritePad(reporter, &writer);
     }
     
     test_ptr(reporter);