add cross-process option to gpipe
add unittests for reader32 and writer32



git-svn-id: http://skia.googlecode.com/svn/trunk@1397 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/include/core/SkReader32.h b/include/core/SkReader32.h
index 03a34c7..654ebd5 100644
--- a/include/core/SkReader32.h
+++ b/include/core/SkReader32.h
@@ -101,7 +101,14 @@
     uint16_t readU16() { return (uint16_t)this->readInt(); }
     int32_t readS32() { return this->readInt(); }
     uint32_t readU32() { return this->readInt(); }
-    
+
+    /**
+     *  Read the length of a string written by SkWriter32::writeString()
+     *  (if len is not NULL) and return the null-ternimated address of the
+     *  string.
+     */
+    const char* readString(size_t* len = NULL);
+
 private:
     // these are always 4-byte aligned
     const char* fCurr;  // current position within buffer
diff --git a/include/core/SkWriter32.h b/include/core/SkWriter32.h
index 8e133c2..c8ebb6a 100644
--- a/include/core/SkWriter32.h
+++ b/include/core/SkWriter32.h
@@ -100,7 +100,22 @@
     }
     
     void writePad(const void* src, size_t size);
-    
+
+    /**
+     *  Writes a string to the writer, which can be retrieved with
+     *  SkReader32::readString().
+     *  The length can be specified, or if -1 is passed, it will be computed by
+     *  calling strlen(). The length must be < 0xFFFF
+     */
+    void writeString(const char* str, size_t len = (size_t)-1);
+
+    /**
+     *  Computes the size (aligned to multiple of 4) need to write the string
+     *  in a call to writeString(). If the length is not specified, it will be
+     *  computed by calling strlen().
+     */
+    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();
diff --git a/include/pipe/SkGPipe.h b/include/pipe/SkGPipe.h
index e9f8502..0ca66ab 100644
--- a/include/pipe/SkGPipe.h
+++ b/include/pipe/SkGPipe.h
@@ -19,6 +19,7 @@
 #define SkGPipe_DEFINED
 
 #include "SkWriter32.h"
+#include "SkFlattenable.h"
 
 class SkCanvas;
 
@@ -74,7 +75,12 @@
     ~SkGPipeWriter();
 
     bool isRecording() const { return NULL != fCanvas; }
-    SkCanvas* startRecording(SkGPipeController*);
+
+    enum Flags {
+        kCrossProcess_Flag = 1 << 0,
+    };
+
+    SkCanvas* startRecording(SkGPipeController*, uint32_t flags = 0);
 
     // called in destructor, but can be called sooner once you know there
     // should be no more drawing calls made into the recording canvas.
@@ -83,6 +89,7 @@
 private:
     class SkGPipeCanvas* fCanvas;
     SkGPipeController*   fController;
+    SkFactorySet         fFactorySet;
     SkWriter32 fWriter;
 };
 
diff --git a/samplecode/SampleApp.cpp b/samplecode/SampleApp.cpp
index 76f4da1..fe998ee 100644
--- a/samplecode/SampleApp.cpp
+++ b/samplecode/SampleApp.cpp
@@ -1470,7 +1470,9 @@
     SimplePC controller(canvas);
     SkGPipeWriter writer;
     if (fUsePipe) {
-        canvas = writer.startRecording(&controller);
+        uint32_t flags = SkGPipeWriter::kCrossProcess_Flag;
+//        flags = 0;
+        canvas = writer.startRecording(&controller, flags);
     }
 #endif
 
diff --git a/src/core/SkWriter32.cpp b/src/core/SkWriter32.cpp
index 141f751..9a60b8e 100644
--- a/src/core/SkWriter32.cpp
+++ b/src/core/SkWriter32.cpp
@@ -185,3 +185,72 @@
     return true;
 }
 
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkReader32.h"
+
+const char* SkReader32::readString(size_t* outLen) {
+    // we need to read at least 1-4 bytes
+    SkASSERT(this->isAvailable(4));
+    const uint8_t* base = (const uint8_t*)this->peek();
+    const uint8_t* ptr = base;
+
+    size_t len = *ptr++;
+    if (0xFF == len) {
+        len = (ptr[0] << 8) | ptr[1];
+        ptr += 2;
+        SkASSERT(len < 0xFFFF);
+    }
+    
+    // skip what we've read, and 0..3 pad bytes
+    // add 1 for the terminating 0 that writeString() included
+    size_t alignedSize = SkAlign4(len + (ptr - base) + 1);
+    this->skip(alignedSize);
+
+    if (outLen) {
+        *outLen = len;
+    }
+    return (const char*)ptr;
+}
+
+void SkWriter32::writeString(const char str[], size_t len) {
+    if ((long)len < 0) {
+        SkASSERT(str);
+        len = strlen(str);
+    }
+    size_t lenBytes = 1;
+    if (len >= 0xFF) {
+        lenBytes = 3;
+        SkASSERT(len < 0xFFFF);
+    }
+    // add 1 since we also write a terminating 0
+    size_t alignedLen = SkAlign4(lenBytes + len + 1);
+    uint8_t* ptr = (uint8_t*)this->reserve(alignedLen);
+    if (1 == lenBytes) {
+        *ptr++ = SkToU8(len);
+    } else {
+        *ptr++ = 0xFF;
+        *ptr++ = SkToU8(len >> 8);
+        *ptr++ = len & 0xFF;
+    }
+    memcpy(ptr, str, len);
+    ptr[len] = 0;
+    // we may have left 0,1,2,3 bytes uninitialized, since we reserved align4
+    // number of bytes. That's ok, since the reader will know to skip those
+}
+
+size_t SkWriter32::WriteStringSize(const char* str, size_t len) {
+    if ((long)len < 0) {
+        SkASSERT(str);
+        len = strlen(str);
+    }
+    size_t lenBytes = 1;
+    if (len >= 0xFF) {
+        lenBytes = 3;
+        SkASSERT(len < 0xFFFF);
+    }
+    // add 1 since we also write a terminating 0
+    return SkAlign4(lenBytes + len + 1);
+}
+
+
diff --git a/src/pipe/SkGPipePriv.h b/src/pipe/SkGPipePriv.h
index 06ba7b6..fb15536 100644
--- a/src/pipe/SkGPipePriv.h
+++ b/src/pipe/SkGPipePriv.h
@@ -75,6 +75,8 @@
     kDef_Typeface_DrawOp,
     kDef_Flattenable_DrawOp,
 
+    kName_Flattenable_DrawOp,   // index <--> name
+
     // these are signals to playback, not drawing verbs
     kDone_DrawOp,
 };
diff --git a/src/pipe/SkGPipeRead.cpp b/src/pipe/SkGPipeRead.cpp
index 5e6c632..c72aa57 100644
--- a/src/pipe/SkGPipeRead.cpp
+++ b/src/pipe/SkGPipeRead.cpp
@@ -70,7 +70,10 @@
     SkGPipeState();
     ~SkGPipeState();
 
-    void setReader(SkFlattenableReadBuffer* reader) { fReader = reader; }
+    void setReader(SkFlattenableReadBuffer* reader) {
+        fReader = reader;
+        fReader->setFactoryPlayback(fFactoryArray.begin(), fFactoryArray.count());
+    }
 
     const SkPaint& paint() const { return fPaint; }
     SkPaint* editPaint() { return &fPaint; }
@@ -83,11 +86,21 @@
     }
 
     void defFlattenable(PaintFlats pf, unsigned index) {
+        SkASSERT(index == fFlatArray.count() + 1);
         SkFlattenable* obj = fReader->readFlattenable();
         *fFlatArray.append() = obj;
-        SkASSERT(index == fFlatArray.count());
     }
 
+    void nameFlattenable(PaintFlats pf, unsigned index) {
+        SkASSERT(index == fFactoryArray.count() + 1);
+        const char* name = fReader->readString();
+        SkFlattenable::Factory fact = SkFlattenable::NameToFactory(name);
+        *fFactoryArray.append() = fact;
+
+        // update this each time we grow the array
+        fReader->setFactoryPlayback(fFactoryArray.begin(), fFactoryArray.count());
+    }
+    
     void addTypeface() {
         size_t size = fReader->readU32();
         const void* data = fReader->skip(SkAlign4(size));
@@ -104,6 +117,7 @@
     SkPaint                   fPaint;
     SkTDArray<SkFlattenable*> fFlatArray;
     SkTDArray<SkTypeface*>    fTypefaces;
+    SkTDArray<SkFlattenable::Factory> fFactoryArray;
 };
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -430,6 +444,13 @@
     state->defFlattenable(pf, index);
 }
 
+static void name_PaintFlat_rp(SkCanvas*, SkReader32*, uint32_t op32,
+                              SkGPipeState* state) {
+    PaintFlats pf = (PaintFlats)DrawOp_unpackFlags(op32);
+    unsigned index = DrawOp_unpackData(op32);
+    state->nameFlattenable(pf, index);
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 static void skip_rp(SkCanvas*, SkReader32* reader, uint32_t op32, SkGPipeState*) {
@@ -476,6 +497,7 @@
     paintOp_rp,
     def_Typeface_rp,
     def_PaintFlat_rp,
+    name_PaintFlat_rp,
 
     done_rp
 };
diff --git a/src/pipe/SkGPipeWrite.cpp b/src/pipe/SkGPipeWrite.cpp
index aa6ed82..5e8ba3d 100644
--- a/src/pipe/SkGPipeWrite.cpp
+++ b/src/pipe/SkGPipeWrite.cpp
@@ -77,7 +77,7 @@
 
 class SkGPipeCanvas : public SkCanvas {
 public:
-    SkGPipeCanvas(SkGPipeController*, SkWriter32*);
+    SkGPipeCanvas(SkGPipeController*, SkWriter32*, SkFactorySet*);
     virtual ~SkGPipeCanvas();
 
     void finish() {
@@ -133,6 +133,7 @@
     virtual void drawData(const void*, size_t);
 
 private:
+    SkFactorySet* fFactorySet;  // optional, only used if cross-process
     SkGPipeController* fController;
     SkWriter32& fWriter;
     size_t      fBlockSize; // amount allocated for writer
@@ -201,7 +202,27 @@
         return 0;
     }
 
+    if (fFactorySet) {
+        uint32_t id = fFactorySet->find((void*)fact);
+        if (0 == id) {
+            const char* name = SkFlattenable::FactoryToName(fact);
+            if (NULL == name) {
+                return 0;
+            }
+            size_t len = strlen(name);
+            size_t size = SkWriter32::WriteStringSize(name, len);
+            if (!this->needOpBytes(size)) {
+                return 0;
+            }
+            unsigned id = fFactorySet->add(fact);
+            this->writeOp(kName_Flattenable_DrawOp, paintflat, id);
+            fWriter.writeString(name, len);
+        }
+    }
+    
     SkFlattenableWriteBuffer tmpWriter(1024);
+    tmpWriter.setFactoryRecorder(fFactorySet);
+
     tmpWriter.writeFlattenable(obj);
     size_t len = tmpWriter.size();
     size_t allocSize = len + sizeof(FlatData);
@@ -236,7 +257,8 @@
 #define MIN_BLOCK_SIZE  (16 * 1024)
 
 SkGPipeCanvas::SkGPipeCanvas(SkGPipeController* controller,
-                             SkWriter32* writer) : fWriter(*writer) {
+                             SkWriter32* writer, SkFactorySet* fset)
+        : fWriter(*writer), fFactorySet(fset) {
     fController = controller;
     fDone = false;
     fBlockSize = 0; // need first block from controller
@@ -768,10 +790,14 @@
     SkSafeUnref(fCanvas);
 }
 
-SkCanvas* SkGPipeWriter::startRecording(SkGPipeController* controller) {
+SkCanvas* SkGPipeWriter::startRecording(SkGPipeController* controller,
+                                        uint32_t flags) {
     if (NULL == fCanvas) {
         fWriter.reset(NULL, 0);
-        fCanvas = SkNEW_ARGS(SkGPipeCanvas, (controller, &fWriter));
+        fFactorySet.reset();
+        fCanvas = SkNEW_ARGS(SkGPipeCanvas, (controller, &fWriter,
+                                             (flags & kCrossProcess_Flag) ?
+                                             &fFactorySet : NULL));
     }
     return fCanvas;
 }
diff --git a/tests/Reader32Test.cpp b/tests/Reader32Test.cpp
new file mode 100644
index 0000000..f57df71
--- /dev/null
+++ b/tests/Reader32Test.cpp
@@ -0,0 +1,93 @@
+/*
+    Copyright 2011 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#include "SkReader32.h"
+#include "Test.h"
+
+static void assert_eof(skiatest::Reporter* reporter, const SkReader32& reader) {
+    REPORTER_ASSERT(reporter, reader.eof());
+    REPORTER_ASSERT(reporter, reader.size() == reader.offset());
+    REPORTER_ASSERT(reporter, (const char*)reader.peek() ==
+                    (const char*)reader.base() + reader.size());
+}
+
+static void assert_start(skiatest::Reporter* reporter, const SkReader32& reader) {
+    REPORTER_ASSERT(reporter, 0 == reader.offset());
+    REPORTER_ASSERT(reporter, reader.size() == reader.available());
+    REPORTER_ASSERT(reporter, reader.isAvailable(reader.size()));
+    REPORTER_ASSERT(reporter, !reader.isAvailable(reader.size() + 1));
+    REPORTER_ASSERT(reporter, reader.peek() == reader.base());
+}
+
+static void assert_empty(skiatest::Reporter* reporter, const SkReader32& reader) {
+    REPORTER_ASSERT(reporter, 0 == reader.size());
+    REPORTER_ASSERT(reporter, 0 == reader.offset());
+    REPORTER_ASSERT(reporter, 0 == reader.available());
+    REPORTER_ASSERT(reporter, !reader.isAvailable(1));
+    assert_eof(reporter, reader);
+    assert_start(reporter, reader);
+}
+
+static void Tests(skiatest::Reporter* reporter) {
+    SkReader32 reader;
+    assert_empty(reporter, reader);
+    REPORTER_ASSERT(reporter, NULL == reader.base());
+    REPORTER_ASSERT(reporter, NULL == reader.peek());
+
+    size_t i;
+
+    const int32_t data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+    const SkScalar data2[] = { 0, SK_Scalar1, -SK_Scalar1, SK_Scalar1/2 };
+    char buffer[SkMax32(sizeof(data), sizeof(data2))];
+
+    reader.setMemory(data, sizeof(data));
+    for (i = 0; i < SK_ARRAY_COUNT(data); ++i) {
+        REPORTER_ASSERT(reporter, sizeof(data) == reader.size());
+        REPORTER_ASSERT(reporter, i*4 == reader.offset());
+        REPORTER_ASSERT(reporter, (const void*)data == reader.base());
+        REPORTER_ASSERT(reporter, (const void*)&data[i] == reader.peek());
+        REPORTER_ASSERT(reporter, data[i] == reader.readInt());
+    }
+    assert_eof(reporter, reader);
+    reader.rewind();
+    assert_start(reporter, reader);
+    reader.read(buffer, sizeof(buffer));
+    REPORTER_ASSERT(reporter, !memcmp(data, buffer, sizeof(buffer)));
+
+    reader.setMemory(data2, sizeof(data2));
+    for (i = 0; i < SK_ARRAY_COUNT(data2); ++i) {
+        REPORTER_ASSERT(reporter, sizeof(data2) == reader.size());
+        REPORTER_ASSERT(reporter, i*4 == reader.offset());
+        REPORTER_ASSERT(reporter, (const void*)data2 == reader.base());
+        REPORTER_ASSERT(reporter, (const void*)&data2[i] == reader.peek());
+        REPORTER_ASSERT(reporter, data2[i] == reader.readScalar());
+    }
+    assert_eof(reporter, reader);
+    reader.rewind();
+    assert_start(reporter, reader);
+    reader.read(buffer, sizeof(buffer));
+    REPORTER_ASSERT(reporter, !memcmp(data2, buffer, sizeof(buffer)));
+
+    reader.setMemory(NULL, 0);
+    assert_empty(reporter, reader);
+    REPORTER_ASSERT(reporter, NULL == reader.base());
+    REPORTER_ASSERT(reporter, NULL == reader.peek());
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("Reader32", Reader32Class, Tests)
+
diff --git a/tests/Writer32Test.cpp b/tests/Writer32Test.cpp
new file mode 100644
index 0000000..63b1209
--- /dev/null
+++ b/tests/Writer32Test.cpp
@@ -0,0 +1,90 @@
+/*
+    Copyright 2011 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#include "SkReader32.h"
+#include "SkWriter32.h"
+#include "Test.h"
+
+static void test1(skiatest::Reporter* reporter, SkWriter32* writer) {
+    const uint32_t data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+    for (size_t i = 0; i < SK_ARRAY_COUNT(data); ++i) {
+        REPORTER_ASSERT(reporter, i*4 == writer->size());
+        writer->write32(data[i]);
+        uint32_t* addr = writer->peek32(i * 4);
+        REPORTER_ASSERT(reporter, data[i] == *addr);
+    }
+
+    char buffer[sizeof(data)];
+    REPORTER_ASSERT(reporter, sizeof(buffer) == writer->size());
+    writer->flatten(buffer);
+    REPORTER_ASSERT(reporter, !memcmp(data, buffer, sizeof(buffer)));
+}
+
+static void test2(skiatest::Reporter* reporter, SkWriter32* writer) {
+    static const char gStr[] = "abcdefghimjklmnopqrstuvwxyz";
+    size_t i;
+
+    size_t len = 0;
+    for (i = 0; i <= 26; ++i) {
+        len += SkWriter32::WriteStringSize(gStr, i);
+        writer->writeString(gStr, i);
+    }
+    REPORTER_ASSERT(reporter, writer->size() == len);
+
+    SkAutoMalloc storage(len);
+    writer->flatten(storage.get());
+
+    SkReader32 reader;
+    reader.setMemory(storage.get(), len);
+    for (i = 0; i <= 26; ++i) {
+        REPORTER_ASSERT(reporter, !reader.eof());
+        const char* str = reader.readString(&len);
+        REPORTER_ASSERT(reporter, i == len);
+        REPORTER_ASSERT(reporter, strlen(str) == len);
+        REPORTER_ASSERT(reporter, !memcmp(str, gStr, len));
+    }
+    REPORTER_ASSERT(reporter, reader.eof());
+}
+
+static void Tests(skiatest::Reporter* reporter) {
+    // dynamic allocator
+    {
+        SkWriter32 writer(256 * 4);
+        REPORTER_ASSERT(reporter, NULL == writer.getSingleBlock());
+        test1(reporter, &writer);
+        
+        writer.reset();
+        test2(reporter, &writer);
+    }
+    
+    // single-block
+    {
+        SkWriter32 writer(0);
+        uint32_t storage[256];
+        REPORTER_ASSERT(reporter, NULL == writer.getSingleBlock());
+        writer.reset(storage, sizeof(storage));
+        REPORTER_ASSERT(reporter, (void*)storage == writer.getSingleBlock());
+        test1(reporter, &writer);
+
+        writer.reset(storage, sizeof(storage));
+        test2(reporter, &writer);
+    }
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("Writer32", Writer32Class, Tests)
+
diff --git a/tests/tests_files.mk b/tests/tests_files.mk
index 28d5fe7..9b90179 100644
--- a/tests/tests_files.mk
+++ b/tests/tests_files.mk
@@ -22,6 +22,7 @@
     ParsePathTest.cpp \
     PathMeasureTest.cpp \
     PathTest.cpp \
+    Reader32Test.cpp \
     RefDictTest.cpp \
     RegionTest.cpp \
     Sk64Test.cpp \
@@ -33,4 +34,5 @@
     Test.cpp \
     TestSize.cpp \
     UtilsTest.cpp \
+    Writer32Test.cpp \
     XfermodeTest.cpp