SkStream: DynamicMemoryWStream gets writeToAndReset

This is similar to copyToAndReset().

Also use this in SkPDF, for minor memory savings:
Single-threaded DM's peak RSS drops from 239MB
to 228MB.

Change-Id: I352a980e6dd54eb05d74cd057bd50e02312753b0
Reviewed-on: https://skia-review.googlesource.com/17714
Reviewed-by: Mike Reed <reed@google.com>
Commit-Queue: Hal Canary <halcanary@google.com>
diff --git a/include/core/SkStream.h b/include/core/SkStream.h
index b4875ad..01fd82a 100644
--- a/include/core/SkStream.h
+++ b/include/core/SkStream.h
@@ -384,11 +384,14 @@
 
     /** More efficient version of read(dst, 0, bytesWritten()). */
     void copyTo(void* dst) const;
-    void writeToStream(SkWStream* dst) const;
+    bool writeToStream(SkWStream* dst) const;
 
     /** Equivalent to copyTo() followed by reset(), but may save memory use. */
     void copyToAndReset(void* dst);
 
+    /** Equivalent to writeToStream() followed by reset(), but may save memory use. */
+    bool writeToAndReset(SkWStream* dst);
+
     /** Return the contents as SkData, and then reset the stream. */
     sk_sp<SkData> detachAsData();
 
diff --git a/src/core/SkStream.cpp b/src/core/SkStream.cpp
index ef86bc9..7bb2079 100644
--- a/src/core/SkStream.cpp
+++ b/src/core/SkStream.cpp
@@ -545,10 +545,13 @@
     }
 }
 
-void SkDynamicMemoryWStream::writeToStream(SkWStream* dst) const {
+bool SkDynamicMemoryWStream::writeToStream(SkWStream* dst) const {
     for (Block* block = fHead; block != nullptr; block = block->fNext) {
-        dst->write(block->start(), block->written());
+        if (!dst->write(block->start(), block->written())) {
+            return false;
+        }
     }
+    return true;
 }
 
 void SkDynamicMemoryWStream::padToAlign4() {
@@ -584,6 +587,23 @@
     fBytesWrittenBeforeTail = 0;
 }
 
+bool SkDynamicMemoryWStream::writeToAndReset(SkWStream* dst) {
+    // By looping through the source and freeing as we copy, we
+    // can reduce real memory use with large streams.
+    bool dstStreamGood = true;
+    for (Block* block = fHead; block != nullptr; ) {
+        if (dstStreamGood && !dst->write(block->start(), block->written())) {
+            dstStreamGood = false;
+        }
+        Block* next = block->fNext;
+        sk_free(block);
+        block = next;
+    }
+    fHead = fTail = nullptr;
+    fBytesWrittenBeforeTail = 0;
+    return dstStreamGood;
+}
+
 sk_sp<SkData> SkDynamicMemoryWStream::detachAsData() {
     const size_t size = this->bytesWritten();
     if (0 == size) {
diff --git a/src/pdf/SkPDFBitmap.cpp b/src/pdf/SkPDFBitmap.cpp
index b58aaf4..d5eb19e 100644
--- a/src/pdf/SkPDFBitmap.cpp
+++ b/src/pdf/SkPDFBitmap.cpp
@@ -356,8 +356,7 @@
     } else {
         bitmap_to_pdf_pixels(bitmap, &deflateWStream);
     }
-    deflateWStream.finalize();  // call before detachAsStream().
-    std::unique_ptr<SkStreamAsset> asset(buffer.detachAsStream());
+    deflateWStream.finalize();  // call before buffer.bytesWritten().
 
     SkPDFDict pdfDict("XObject");
     pdfDict.insertName("Subtype", "Image");
@@ -380,11 +379,11 @@
     }
     pdfDict.insertInt("BitsPerComponent", 8);
     pdfDict.insertName("Filter", "FlateDecode");
-    pdfDict.insertInt("Length", asset->getLength());
+    pdfDict.insertInt("Length", buffer.bytesWritten());
     pdfDict.emitObject(stream, objNumMap);
 
     pdf_stream_begin(stream);
-    stream->writeStream(asset.get(), asset->getLength());
+    buffer.writeToAndReset(stream);
     pdf_stream_end(stream);
 }
 
diff --git a/src/pdf/SkPDFTypes.cpp b/src/pdf/SkPDFTypes.cpp
index c66c80a..2559f93 100644
--- a/src/pdf/SkPDFTypes.cpp
+++ b/src/pdf/SkPDFTypes.cpp
@@ -489,7 +489,7 @@
     SkPDFUnion::Name("FlateDecode").emitObject(stream, objNumMap);
     stream->writeText(">>");
     stream->writeText(" stream\n");
-    buffer.writeToStream(stream);
+    buffer.writeToAndReset(stream);
     stream->writeText("\nendstream");
 }
 #endif