From 48305e835351291ef72a7084c6c1af7b185d89c0 Mon Sep 17 00:00:00 2001 From: halcanary Date: Tue, 18 Aug 2015 13:30:25 -0700 Subject: [PATCH] SkPDF/Deflate: clean up old SkFlate code Factor out some of https://crrev.com/1227913008 BUG=skia:3030 Review URL: https://codereview.chromium.org/1298243002 --- gyp/tests.gypi | 3 +- src/core/SkStream.cpp | 22 ++++++++ src/core/SkStreamPriv.h | 6 +++ src/pdf/SkDeflate.cpp | 109 +------------------------------------- src/pdf/SkDeflate.h | 30 ----------- src/pdf/SkPDFStream.cpp | 6 +-- tests/PDFDeflateWStreamTest.cpp | 100 ++++++++++++++++++++++++++++++++--- tests/PDFFlateTest.cpp | 114 ---------------------------------------- tests/PDFPrimitivesTest.cpp | 8 +-- tests/StreamTest.cpp | 58 ++++++++++++++++++++ 10 files changed, 191 insertions(+), 265 deletions(-) delete mode 100644 tests/PDFFlateTest.cpp diff --git a/gyp/tests.gypi b/gyp/tests.gypi index ad6a56c..1fd87ad 100644 --- a/gyp/tests.gypi +++ b/gyp/tests.gypi @@ -27,6 +27,7 @@ 'tools.gyp:picture_utils', 'tools.gyp:resources', 'tools.gyp:sk_tool_utils', + 'zlib.gyp:zlib', ], 'conditions': [ [ 'skia_os not in ["linux", "freebsd", "openbsd", "solaris", "chromeos", "android"]', { @@ -38,7 +39,7 @@ ], }], [ 'not skia_pdf', { - 'dependencies!': [ 'pdf.gyp:pdf' ], + 'dependencies!': [ 'pdf.gyp:pdf', 'zlib.gyp:zlib' ], 'dependencies': [ 'pdf.gyp:nopdf' ], 'sources!': [ '(input->getMemoryBase()); + if (base && input->hasPosition() && input->hasLength()) { + // Shortcut that avoids the while loop. + size_t position = input->getPosition(); + size_t length = input->getLength(); + SkASSERT(length >= position); + return out->write(&base[position], length - position); + } + char scratch[4096]; + size_t count; + while (true) { + count = input->read(scratch, sizeof(scratch)); + if (0 == count) { + return true; + } + if (!out->write(scratch, count)) { + return false; + } + } +} diff --git a/src/core/SkStreamPriv.h b/src/core/SkStreamPriv.h index 718097d..d1af117 100644 --- a/src/core/SkStreamPriv.h +++ b/src/core/SkStreamPriv.h @@ -43,4 +43,10 @@ SkData *SkCopyStreamToData(SkStream* stream); */ SkStreamRewindable* SkStreamRewindableFromSkStream(SkStream* stream); +/** + * Copies the input stream from the current position to the end. + * Does not rewind the input stream. + */ +bool SkStreamCopy(SkWStream* out, SkStream* input); + #endif // SkStreamPriv_DEFINED diff --git a/src/pdf/SkDeflate.cpp b/src/pdf/SkDeflate.cpp index 0953ef2..2ca516c 100644 --- a/src/pdf/SkDeflate.cpp +++ b/src/pdf/SkDeflate.cpp @@ -19,120 +19,15 @@ namespace { #include "zlib.h" #endif -// static -const size_t kBufferSize = 1024; - // Different zlib implementations use different T. // We've seen size_t and unsigned. template void* skia_alloc_func(void*, T items, T size) { return sk_calloc_throw(SkToSizeT(items) * SkToSizeT(size)); } -static void skia_free_func(void*, void* address) { sk_free(address); } - -bool doFlate(bool compress, SkStream* src, SkWStream* dst) { - uint8_t inputBuffer[kBufferSize]; - uint8_t outputBuffer[kBufferSize]; - z_stream flateData; - flateData.zalloc = &skia_alloc_func; - flateData.zfree = &skia_free_func; - flateData.opaque = NULL; - flateData.next_in = NULL; - flateData.avail_in = 0; - flateData.next_out = outputBuffer; - flateData.avail_out = kBufferSize; - int rc; - if (compress) - rc = deflateInit(&flateData, Z_DEFAULT_COMPRESSION); - else - rc = inflateInit(&flateData); - if (rc != Z_OK) - return false; - - uint8_t* input = (uint8_t*)src->getMemoryBase(); - size_t inputLength = src->getLength(); - if (input == NULL || inputLength == 0) { - input = NULL; - flateData.next_in = inputBuffer; - flateData.avail_in = 0; - } else { - flateData.next_in = input; - flateData.avail_in = SkToUInt(inputLength); - } - - rc = Z_OK; - while (true) { - if (flateData.avail_out < kBufferSize) { - if (!dst->write(outputBuffer, kBufferSize - flateData.avail_out)) { - rc = Z_BUF_ERROR; - break; - } - flateData.next_out = outputBuffer; - flateData.avail_out = kBufferSize; - } - if (rc != Z_OK) - break; - if (flateData.avail_in == 0) { - if (input != NULL) - break; - size_t read = src->read(&inputBuffer, kBufferSize); - if (read == 0) - break; - flateData.next_in = inputBuffer; - flateData.avail_in = SkToUInt(read); - } - if (compress) - rc = deflate(&flateData, Z_NO_FLUSH); - else - rc = inflate(&flateData, Z_NO_FLUSH); - } - while (rc == Z_OK) { - if (compress) - rc = deflate(&flateData, Z_FINISH); - else - rc = inflate(&flateData, Z_FINISH); - if (flateData.avail_out < kBufferSize) { - if (!dst->write(outputBuffer, kBufferSize - flateData.avail_out)) - return false; - flateData.next_out = outputBuffer; - flateData.avail_out = kBufferSize; - } - } - - if (compress) - deflateEnd(&flateData); - else - inflateEnd(&flateData); - if (rc == Z_STREAM_END) - return true; - return false; -} - -} - -// static -bool SkFlate::Deflate(SkStream* src, SkWStream* dst) { - return doFlate(true, src, dst); -} - -bool SkFlate::Deflate(const void* ptr, size_t len, SkWStream* dst) { - SkMemoryStream stream(ptr, len); - return doFlate(true, &stream, dst); -} - -bool SkFlate::Deflate(const SkData* data, SkWStream* dst) { - if (data) { - SkMemoryStream stream(data->data(), data->size()); - return doFlate(true, &stream, dst); - } - return false; -} - -// static -bool SkFlate::Inflate(SkStream* src, SkWStream* dst) { - return doFlate(false, src, dst); -} +void skia_free_func(void*, void* address) { sk_free(address); } +} // namespace #define SKDEFLATEWSTREAM_INPUT_BUFFER_SIZE 4096 #define SKDEFLATEWSTREAM_OUTPUT_BUFFER_SIZE 4224 // 4096 + 128, usually big diff --git a/src/pdf/SkDeflate.h b/src/pdf/SkDeflate.h index 0104c45..d732378 100644 --- a/src/pdf/SkDeflate.h +++ b/src/pdf/SkDeflate.h @@ -13,36 +13,6 @@ #include "SkTypes.h" #include "SkStream.h" -class SkData; - -/** \class SkFlate - A class to provide access to the flate compression algorithm. -*/ -class SkFlate { -public: - /** - * Use the flate compression algorithm to compress the data in src, - * putting the result into dst. Returns false if an error occurs. - */ - static bool Deflate(SkStream* src, SkWStream* dst); - - /** - * Use the flate compression algorithm to compress the data in src, - * putting the result into dst. Returns false if an error occurs. - */ - static bool Deflate(const void* src, size_t len, SkWStream* dst); - - /** - * Use the flate compression algorithm to compress the data, - * putting the result into dst. Returns false if an error occurs. - */ - static bool Deflate(const SkData*, SkWStream* dst); - - /** Use the flate compression algorithm to decompress the data in src, - putting the result into dst. Returns false if an error occurs. - */ - static bool Inflate(SkStream* src, SkWStream* dst); -}; /** * Wrap a stream in this class to compress the information written to diff --git a/src/pdf/SkPDFStream.cpp b/src/pdf/SkPDFStream.cpp index e709357..d21205c 100644 --- a/src/pdf/SkPDFStream.cpp +++ b/src/pdf/SkPDFStream.cpp @@ -29,9 +29,9 @@ void SkPDFStream::emitObject(SkWStream* stream, if (fState == kUnused_State) { fState = kNoCompression_State; SkDynamicMemoryWStream compressedData; - - SkAssertResult( - SkFlate::Deflate(fDataStream.get(), &compressedData)); + SkDeflateWStream deflateWStream(&compressedData); + SkAssertResult(SkStreamCopy(&deflateWStream, fDataStream.get())); + deflateWStream.finalize(); SkAssertResult(fDataStream->rewind()); if (compressedData.getOffset() < this->dataSize()) { SkAutoTDelete compressed( diff --git a/tests/PDFDeflateWStreamTest.cpp b/tests/PDFDeflateWStreamTest.cpp index bc0defc..a85212c 100644 --- a/tests/PDFDeflateWStreamTest.cpp +++ b/tests/PDFDeflateWStreamTest.cpp @@ -9,6 +9,98 @@ #include "SkRandom.h" #include "Test.h" +namespace { + +#ifdef ZLIB_INCLUDE + #include ZLIB_INCLUDE +#else + #include "zlib.h" +#endif + +// Different zlib implementations use different T. +// We've seen size_t and unsigned. +template void* skia_alloc_func(void*, T items, T size) { + return sk_calloc_throw(SkToSizeT(items) * SkToSizeT(size)); +} + +void skia_free_func(void*, void* address) { sk_free(address); } + +/** + * Use the un-deflate compression algorithm to decompress the data in src, + * returning the result. Returns NULL if an error occurs. + */ +SkStreamAsset* stream_inflate(SkStream* src) { + SkDynamicMemoryWStream decompressedDynamicMemoryWStream; + SkWStream* dst = &decompressedDynamicMemoryWStream; + + static const size_t kBufferSize = 1024; + uint8_t inputBuffer[kBufferSize]; + uint8_t outputBuffer[kBufferSize]; + z_stream flateData; + flateData.zalloc = &skia_alloc_func; + flateData.zfree = &skia_free_func; + flateData.opaque = NULL; + flateData.next_in = NULL; + flateData.avail_in = 0; + flateData.next_out = outputBuffer; + flateData.avail_out = kBufferSize; + int rc; + rc = inflateInit(&flateData); + if (rc != Z_OK) + return nullptr; + + uint8_t* input = (uint8_t*)src->getMemoryBase(); + size_t inputLength = src->getLength(); + if (input == NULL || inputLength == 0) { + input = NULL; + flateData.next_in = inputBuffer; + flateData.avail_in = 0; + } else { + flateData.next_in = input; + flateData.avail_in = SkToUInt(inputLength); + } + + rc = Z_OK; + while (true) { + if (flateData.avail_out < kBufferSize) { + if (!dst->write(outputBuffer, kBufferSize - flateData.avail_out)) { + rc = Z_BUF_ERROR; + break; + } + flateData.next_out = outputBuffer; + flateData.avail_out = kBufferSize; + } + if (rc != Z_OK) + break; + if (flateData.avail_in == 0) { + if (input != NULL) + break; + size_t read = src->read(&inputBuffer, kBufferSize); + if (read == 0) + break; + flateData.next_in = inputBuffer; + flateData.avail_in = SkToUInt(read); + } + rc = inflate(&flateData, Z_NO_FLUSH); + } + while (rc == Z_OK) { + rc = inflate(&flateData, Z_FINISH); + if (flateData.avail_out < kBufferSize) { + if (!dst->write(outputBuffer, kBufferSize - flateData.avail_out)) + return nullptr; + flateData.next_out = outputBuffer; + flateData.avail_out = kBufferSize; + } + } + + inflateEnd(&flateData); + if (rc != Z_STREAM_END) { + return nullptr; + } + return decompressedDynamicMemoryWStream.detachAsStream(); +} +} // namespace + DEF_TEST(SkDeflateWStream, r) { SkRandom random(123456); for (int i = 0; i < 50; ++i) { @@ -34,13 +126,7 @@ DEF_TEST(SkDeflateWStream, r) { } SkAutoTDelete compressed( dynamicMemoryWStream.detachAsStream()); - - SkDynamicMemoryWStream decompressedDynamicMemoryWStream; - SkAssertResult(SkFlate::Inflate(compressed, - &decompressedDynamicMemoryWStream)); - - SkAutoTDelete decompressed( - decompressedDynamicMemoryWStream.detachAsStream()); + SkAutoTDelete decompressed(stream_inflate(compressed)); if (decompressed->getLength() != size) { ERRORF(r, "Decompression failed to get right size [%d]." diff --git a/tests/PDFFlateTest.cpp b/tests/PDFFlateTest.cpp deleted file mode 100644 index 64dc4e2..0000000 --- a/tests/PDFFlateTest.cpp +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2011 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkData.h" -#include "SkDeflate.h" -#include "SkStream.h" -#include "Test.h" - -// A memory stream that reports zero size with the standard call, like -// an unseekable file stream would. -class SkZeroSizeMemStream : public SkMemoryStream { -public: - virtual size_t read(void* buffer, size_t size) { - if (buffer == NULL && size == 0) - return 0; - if (buffer == NULL && size == kGetSizeKey) - size = 0; - return SkMemoryStream::read(buffer, size); - } - - static const size_t kGetSizeKey = 0xDEADBEEF; -}; - -// Returns a deterministic data of the given size that should be -// very compressible. -static SkData* new_test_data(size_t dataSize) { - SkAutoTMalloc testBuffer(dataSize); - for (size_t i = 0; i < dataSize; ++i) { - testBuffer[SkToInt(i)] = i % 64; - } - return SkData::NewFromMalloc(testBuffer.detach(), dataSize); -} - -static void TestFlate(skiatest::Reporter* reporter, SkMemoryStream* testStream, - size_t dataSize) { - SkASSERT(testStream != NULL); - - SkAutoDataUnref testData(new_test_data(dataSize)); - SkASSERT(testData->size() == dataSize); - - testStream->setMemory(testData->data(), dataSize, /*copyData=*/ true); - SkDynamicMemoryWStream compressed; - bool deflateSuccess = SkFlate::Deflate(testStream, &compressed); - REPORTER_ASSERT(reporter, deflateSuccess); - - // Check that the input data wasn't changed. - size_t inputSize = testStream->getLength(); - if (inputSize == 0) { - inputSize = testStream->read(NULL, SkZeroSizeMemStream::kGetSizeKey); - } - REPORTER_ASSERT(reporter, dataSize == inputSize); - if (dataSize == inputSize) { - REPORTER_ASSERT(reporter, memcmp(testData->data(), - testStream->getMemoryBase(), - dataSize) == 0); - } - - size_t compressedSize = compressed.getOffset(); - - SkAutoDataUnref compressedData(compressed.copyToData()); - testStream->setData(compressedData.get()); - - SkDynamicMemoryWStream uncompressed; - bool inflateSuccess = SkFlate::Inflate(testStream, &uncompressed); - REPORTER_ASSERT(reporter, inflateSuccess); - - // Check that the input data wasn't changed. - inputSize = testStream->getLength(); - if (inputSize == 0) { - inputSize = testStream->read(NULL, SkZeroSizeMemStream::kGetSizeKey); - } - REPORTER_ASSERT(reporter, compressedSize == inputSize); - if (compressedData->size() == inputSize) { - REPORTER_ASSERT(reporter, memcmp(testStream->getMemoryBase(), - compressedData->data(), - compressedData->size()) == 0); - } - - // Check that the uncompressed data matches the source data. - SkAutoDataUnref uncompressedData(uncompressed.copyToData()); - REPORTER_ASSERT(reporter, dataSize == uncompressedData->size()); - if (dataSize == uncompressedData->size()) { - REPORTER_ASSERT(reporter, memcmp(testData->data(), - uncompressedData->data(), - dataSize) == 0); - } - - if (compressedSize < 1) { return; } - - double compressionRatio = static_cast(dataSize) / compressedSize; - // Assert that some compression took place. - REPORTER_ASSERT(reporter, compressionRatio > 1.2); - - if (reporter->verbose()) { - SkDebugf("Flate Test: \t input size: " SK_SIZE_T_SPECIFIER - "\tcompressed size: " SK_SIZE_T_SPECIFIER - "\tratio: %.4g\n", - dataSize, compressedSize, compressionRatio); - } -} - -DEF_TEST(Flate, reporter) { - SkMemoryStream memStream; - TestFlate(reporter, &memStream, 512); - TestFlate(reporter, &memStream, 10240); - - SkZeroSizeMemStream fileStream; - TestFlate(reporter, &fileStream, 512); - TestFlate(reporter, &fileStream, 10240); -} diff --git a/tests/PDFPrimitivesTest.cpp b/tests/PDFPrimitivesTest.cpp index b0b7765..1d35c08 100644 --- a/tests/PDFPrimitivesTest.cpp +++ b/tests/PDFPrimitivesTest.cpp @@ -99,12 +99,14 @@ static void TestPDFStream(skiatest::Reporter* reporter) { SkAutoTUnref stream(new SkPDFStream(streamData2.get())); SkDynamicMemoryWStream compressedByteStream; - SkFlate::Deflate(streamData2.get(), &compressedByteStream); - SkAutoDataUnref compressedData(compressedByteStream.copyToData()); + SkDeflateWStream deflateWStream(&compressedByteStream); + deflateWStream.write(streamBytes2, strlen(streamBytes2)); + deflateWStream.finalize(); SkDynamicMemoryWStream expected; expected.writeText("<> stream\n"); - expected.write(compressedData->data(), compressedData->size()); + compressedByteStream.writeToStream(&expected); + compressedByteStream.reset(); expected.writeText("\nendstream"); SkAutoDataUnref expectedResultData2(expected.copyToData()); SkString result = emit_to_string(*stream); diff --git a/tests/StreamTest.cpp b/tests/StreamTest.cpp index 926cfc2..78c0e50 100644 --- a/tests/StreamTest.cpp +++ b/tests/StreamTest.cpp @@ -11,6 +11,7 @@ #include "SkOSFile.h" #include "SkRandom.h" #include "SkStream.h" +#include "SkStreamPriv.h" #include "Test.h" #ifndef SK_BUILD_FOR_WIN @@ -335,3 +336,60 @@ DEF_TEST(StreamPeek_BlockMemoryStream, rep) { } stream_peek_test(rep, asset, expected); } + +namespace { +class DumbStream : public SkStream { +public: + DumbStream(const uint8_t* data, size_t n) + : fData(data), fCount(n), fIdx(0) {} + size_t read(void* buffer, size_t size) override { + size_t c = SkTMin(fCount - fIdx, size); + if (c) { + memcpy(buffer, &fData[fIdx], size); + fIdx += c; + } + return c; + } + bool isAtEnd() const override { + return fCount > fIdx; + } + private: + const uint8_t* fData; + size_t fCount, fIdx; +}; +} // namespace + +static void stream_copy_test(skiatest::Reporter* reporter, + const void* srcData, + size_t N, + SkStream* stream) { + SkDynamicMemoryWStream tgt; + if (!SkStreamCopy(&tgt, stream)) { + ERRORF(reporter, "SkStreamCopy failed"); + return; + } + SkAutoTUnref data(tgt.copyToData()); + tgt.reset(); + if (data->size() != N) { + ERRORF(reporter, "SkStreamCopy incorrect size"); + return; + } + if (0 != memcmp(data->data(), srcData, N)) { + ERRORF(reporter, "SkStreamCopy bad copy"); + } +} + +DEF_TEST(StreamCopy, reporter) { + SkRandom random(123456); + static const size_t N = 10000; + uint8_t src[N]; + for (size_t j = 0; j < N; ++j) { + src[j] = random.nextU() & 0xff; + } + // SkStreamCopy had two code paths; this test both. + DumbStream dumbStream(src, N); + stream_copy_test(reporter, src, N, &dumbStream); + SkMemoryStream smartStream(src, N); + stream_copy_test(reporter, src, N, &smartStream); + +} -- 2.7.4