Add a detachAsStream to SkDynamicMemoryWStream.
authorbungeman@google.com <bungeman@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Thu, 18 Jul 2013 22:27:21 +0000 (22:27 +0000)
committerbungeman@google.com <bungeman@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Thu, 18 Jul 2013 22:27:21 +0000 (22:27 +0000)
R=reed@google.com

Review URL: https://codereview.chromium.org/19677002

git-svn-id: http://skia.googlecode.com/svn/trunk@10171 2bbb7eff-a529-9590-31e7-b0007b416f81

gm/gmmain.cpp
include/core/SkStream.h
src/core/SkStream.cpp
tests/StreamTest.cpp

index 98525a5..3c01156 100644 (file)
@@ -410,11 +410,9 @@ public:
         gm_fprintf(stdout, "(results marked with [*] will cause nonzero return value)\n");
     }
 
-    static bool write_document(const SkString& path,
-                               const SkDynamicMemoryWStream& document) {
+    static bool write_document(const SkString& path, SkStreamAsset* asset) {
         SkFILEWStream stream(path.c_str());
-        SkAutoDataUnref data(document.copyToData());
-        return stream.write(data->data(), data->size());
+        return stream.writeStream(asset, asset->getLength());
     }
 
     /**
@@ -665,7 +663,7 @@ public:
                                            const char renderModeDescriptor [],
                                            const char *shortName,
                                            const BitmapAndDigest* bitmapAndDigest,
-                                           SkDynamicMemoryWStream* document) {
+                                           SkStreamAsset* document) {
         SkString path;
         bool success = false;
         if (gRec.fBackend == kRaster_Backend ||
@@ -679,12 +677,12 @@ public:
         if (kPDF_Backend == gRec.fBackend) {
             path = make_filename(writePath, shortName, gRec.fName, renderModeDescriptor,
                                  "pdf");
-            success = write_document(path, *document);
+            success = write_document(path, document);
         }
         if (kXPS_Backend == gRec.fBackend) {
             path = make_filename(writePath, shortName, gRec.fName, renderModeDescriptor,
                                  "xps");
-            success = write_document(path, *document);
+            success = write_document(path, document);
         }
         if (success) {
             return kEmpty_ErrorCombination;
@@ -911,7 +909,7 @@ public:
      */
     ErrorCombination compare_test_results_to_stored_expectations(
         GM* gm, const ConfigData& gRec, const char writePath[],
-        const BitmapAndDigest* actualBitmapAndDigest, SkDynamicMemoryWStream* document) {
+        const BitmapAndDigest* actualBitmapAndDigest, SkStreamAsset* document) {
 
         SkString shortNamePlusConfig = make_shortname_plus_config(gm->shortName(), gRec.fName);
         SkString nameWithExtension(shortNamePlusConfig);
@@ -1025,24 +1023,10 @@ public:
     }
 
     static SkPicture* stream_to_new_picture(const SkPicture& src) {
-
-        // To do in-memory commiunications with a stream, we need to:
-        // * create a dynamic memory stream
-        // * copy it into a buffer
-        // * create a read stream from it
-        // ?!?!
-
         SkDynamicMemoryWStream storage;
         src.serialize(&storage);
-
-        size_t streamSize = storage.getOffset();
-        SkAutoMalloc dstStorage(streamSize);
-        void* dst = dstStorage.get();
-        //char* dst = new char [streamSize];
-        //@todo thudson 22 April 2011 when can we safely delete [] dst?
-        storage.copyTo(dst);
-        SkMemoryStream pictReadback(dst, streamSize);
-        SkPicture* retval = SkPicture::CreateFromStream(&pictReadback);
+        SkAutoTUnref<SkStreamAsset> pictReadback(storage.detatchAsStream());
+        SkPicture* retval = SkPicture::CreateFromStream(pictReadback);
         return retval;
     }
 
@@ -1079,13 +1063,14 @@ public:
             bitmap = NULL;  // we don't generate a bitmap rendering of the XPS file
         }
 
+        SkAutoTUnref<SkStreamAsset> documentStream(document.detatchAsStream());
         if (NULL == bitmap) {
             return compare_test_results_to_stored_expectations(
-                gm, gRec, writePath, NULL, &document);
+                gm, gRec, writePath, NULL, documentStream);
         } else {
             BitmapAndDigest bitmapAndDigest(*bitmap);
             return compare_test_results_to_stored_expectations(
-                gm, gRec, writePath, &bitmapAndDigest, &document);
+                gm, gRec, writePath, &bitmapAndDigest, documentStream);
         }
     }
 
@@ -1093,8 +1078,6 @@ public:
                                            const ConfigData& gRec,
                                            const SkBitmap& referenceBitmap,
                                            GrSurface* gpuTarget) {
-        SkDynamicMemoryWStream document;
-
         if (gRec.fBackend == kRaster_Backend ||
             gRec.fBackend == kGPU_Backend) {
             const char renderModeDescriptor[] = "-deferred";
index 31ed7bc..1930f3f 100644 (file)
@@ -280,7 +280,7 @@ public:
 
     virtual size_t getLength() const SK_OVERRIDE;
 
-    const void* getMemoryBase() SK_OVERRIDE;
+    virtual const void* getMemoryBase() SK_OVERRIDE;
 
 private:
     SkFILE*     fFILE;
@@ -423,7 +423,10 @@ public:
      */
     SkData* copyToData() const;
 
-    // reset the stream to its original state
+    /** Reset, returning a reader stream with the current content. */
+    SkStreamAsset* detatchAsStream();
+
+    /** Reset the stream to its original, empty, state. */
     void reset();
     void padToAlign4();
 private:
@@ -435,6 +438,10 @@ private:
 
     void invalidateCopy();
 
+    // For access to the Block type.
+    friend class SkBlockMemoryStream;
+    friend class SkBlockMemoryRefCnt;
+
     typedef SkWStream INHERITED;
 };
 
index 91de88a..b251905 100644 (file)
@@ -661,6 +661,138 @@ void SkDynamicMemoryWStream::invalidateCopy() {
     }
 }
 
+class SkBlockMemoryRefCnt : public SkRefCnt {
+public:
+    explicit SkBlockMemoryRefCnt(SkDynamicMemoryWStream::Block* head) : fHead(head) { }
+
+    virtual ~SkBlockMemoryRefCnt() {
+        SkDynamicMemoryWStream::Block* block = fHead;
+        while (block != NULL) {
+            SkDynamicMemoryWStream::Block* next = block->fNext;
+            sk_free(block);
+            block = next;
+        }
+    }
+
+    SkDynamicMemoryWStream::Block* const fHead;
+};
+
+class SkBlockMemoryStream : public SkStreamAsset {
+public:
+    SkBlockMemoryStream(SkDynamicMemoryWStream::Block* head, size_t size)
+        : fBlockMemory(SkNEW_ARGS(SkBlockMemoryRefCnt, (head))), fCurrent(head)
+        , fSize(size) , fOffset(0), fCurrentOffset(0) { }
+
+    SkBlockMemoryStream(SkBlockMemoryRefCnt* headRef, size_t size)
+        : fBlockMemory(SkRef(headRef)), fCurrent(fBlockMemory->fHead)
+        , fSize(size) , fOffset(0), fCurrentOffset(0) { }
+
+    virtual size_t read(void* buffer, size_t rawCount) SK_OVERRIDE {
+        size_t count = rawCount;
+        if (fOffset + count > fSize) {
+            count = fSize - fOffset;
+        }
+        size_t bytesLeftToRead = count;
+        while (fCurrent != NULL) {
+            size_t bytesLeftInCurrent = fCurrent->written() - fCurrentOffset;
+            size_t bytesFromCurrent = bytesLeftToRead <= bytesLeftInCurrent
+                                    ? bytesLeftToRead  : bytesLeftInCurrent;
+            if (buffer) {
+                memcpy(buffer, fCurrent->start() + fCurrentOffset, bytesFromCurrent);
+            }
+            if (bytesLeftToRead <= bytesFromCurrent) {
+                fCurrentOffset += bytesFromCurrent;
+                fOffset += count;
+                return count;
+            }
+            bytesLeftToRead -= bytesFromCurrent;
+            buffer = SkTAddOffset<void>(buffer, bytesFromCurrent);
+            fCurrent = fCurrent->fNext;
+            fCurrentOffset = 0;
+        }
+        SkASSERT(false);
+        return 0;
+    }
+
+    virtual bool isAtEnd() const SK_OVERRIDE {
+        return fOffset == fSize;
+    }
+
+    virtual bool rewind() SK_OVERRIDE {
+        fCurrent = fBlockMemory->fHead;
+        fOffset = 0;
+        fCurrentOffset = 0;
+        return true;
+    }
+
+    virtual SkBlockMemoryStream* duplicate() const SK_OVERRIDE {
+        return SkNEW_ARGS(SkBlockMemoryStream, (fBlockMemory.get(), fSize));
+    }
+
+    virtual size_t getPosition() const SK_OVERRIDE {
+        return fOffset;
+    }
+
+    virtual bool seek(size_t position) SK_OVERRIDE {
+        // If possible, skip forward.
+        if (position >= fOffset) {
+            size_t skipAmount = position - fOffset;
+            return this->skip(skipAmount) == skipAmount;
+        }
+        // If possible, move backward within the current block.
+        size_t moveBackAmount = fOffset - position;
+        if (moveBackAmount <= fCurrentOffset) {
+            fCurrentOffset -= moveBackAmount;
+            fOffset -= moveBackAmount;
+            return true;
+        }
+        // Otherwise rewind and move forward.
+        return this->rewind() && this->skip(position) == position;
+    }
+
+    virtual bool move(long offset) SK_OVERRIDE {
+        return seek(fOffset + offset);
+    }
+
+    virtual SkBlockMemoryStream* fork() const SK_OVERRIDE {
+        SkAutoTUnref<SkBlockMemoryStream> that(this->duplicate());
+        that->fCurrent = this->fCurrent;
+        that->fOffset = this->fOffset;
+        that->fCurrentOffset = this->fCurrentOffset;
+        return that.detach();
+    }
+
+    virtual size_t getLength() const SK_OVERRIDE {
+        return fSize;
+    }
+
+    virtual const void* getMemoryBase() SK_OVERRIDE {
+        if (NULL == fBlockMemory->fHead->fNext) {
+            return fBlockMemory->fHead->start();
+        }
+        return NULL;
+    }
+
+private:
+    SkAutoTUnref<SkBlockMemoryRefCnt> const fBlockMemory;
+    SkDynamicMemoryWStream::Block const * fCurrent;
+    size_t const fSize;
+    size_t fOffset;
+    size_t fCurrentOffset;
+};
+
+SkStreamAsset* SkDynamicMemoryWStream::detatchAsStream() {
+    if (fCopy) {
+        SkMemoryStream* stream = SkNEW_ARGS(SkMemoryStream, (fCopy));
+        this->reset();
+        return stream;
+    }
+    SkBlockMemoryStream* stream = SkNEW_ARGS(SkBlockMemoryStream, (fHead, fBytesWritten));
+    fHead = 0;
+    this->reset();
+    return stream;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 void SkDebugWStream::newline()
index cf82843..b3a427f 100644 (file)
@@ -30,6 +30,7 @@ static void test_loop_stream(skiatest::Reporter* reporter, SkStream* stream,
     }
 
     // expect EOF
+    REPORTER_ASSERT(reporter, stream->isAtEnd());
     size_t bytes = stream->read(tmp, 1);
     REPORTER_ASSERT(reporter, 0 == bytes);
 }
@@ -81,6 +82,7 @@ static void TestWStream(skiatest::Reporter* reporter) {
         REPORTER_ASSERT(reporter, ds.write(s, 26));
     }
     REPORTER_ASSERT(reporter, ds.getOffset() == 100 * 26);
+
     char* dst = new char[100 * 26 + 1];
     dst[100*26] = '*';
     ds.copyTo(dst);
@@ -90,10 +92,42 @@ static void TestWStream(skiatest::Reporter* reporter) {
     }
 
     {
-        SkData* data = ds.copyToData();
+        SkAutoTUnref<SkStreamAsset> stream(ds.detatchAsStream());
+        REPORTER_ASSERT(reporter, 100 * 26 == stream->getLength());
+        REPORTER_ASSERT(reporter, ds.getOffset() == 0);
+        test_loop_stream(reporter, stream.get(), s, 26, 100);
+
+        SkAutoTUnref<SkStreamAsset> stream2(stream->duplicate());
+        test_loop_stream(reporter, stream2.get(), s, 26, 100);
+
+        SkAutoTUnref<SkStreamAsset> stream3(stream->fork());
+        REPORTER_ASSERT(reporter, stream3->isAtEnd());
+        char tmp;
+        size_t bytes = stream->read(&tmp, 1);
+        REPORTER_ASSERT(reporter, 0 == bytes);
+        stream3->rewind();
+        test_loop_stream(reporter, stream3.get(), s, 26, 100);
+    }
+
+    for (i = 0; i < 100; i++) {
+        REPORTER_ASSERT(reporter, ds.write(s, 26));
+    }
+    REPORTER_ASSERT(reporter, ds.getOffset() == 100 * 26);
+
+    {
+        SkAutoTUnref<SkData> data(ds.copyToData());
         REPORTER_ASSERT(reporter, 100 * 26 == data->size());
         REPORTER_ASSERT(reporter, memcmp(dst, data->data(), data->size()) == 0);
-        data->unref();
+    }
+
+    {
+        // Test that this works after a copyToData.
+        SkAutoTUnref<SkStreamAsset> stream(ds.detatchAsStream());
+        REPORTER_ASSERT(reporter, ds.getOffset() == 0);
+        test_loop_stream(reporter, stream.get(), s, 26, 100);
+
+        SkAutoTUnref<SkStreamAsset> stream2(stream->duplicate());
+        test_loop_stream(reporter, stream2.get(), s, 26, 100);
     }
     delete[] dst;