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
This commit is contained in:
halcanary 2015-08-18 13:30:25 -07:00 committed by Commit bot
parent a44919ea27
commit 48305e8353
10 changed files with 191 additions and 265 deletions

View File

@ -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!': [ '<!@(python find.py ../tests "PDF*.c*")', ],
}],

View File

@ -950,3 +950,25 @@ SkStreamRewindable* SkStreamRewindableFromSkStream(SkStream* stream) {
return tempStream.detachAsStream(); // returns a SkBlockMemoryStream,
// cheaper than copying to SkData
}
bool SkStreamCopy(SkWStream* out, SkStream* input) {
const char* base = static_cast<const char*>(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;
}
}
}

View File

@ -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

View File

@ -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 <typename T> 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

View File

@ -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

View File

@ -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<SkStream> compressed(

View File

@ -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 <typename T> 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<SkStreamAsset> compressed(
dynamicMemoryWStream.detachAsStream());
SkDynamicMemoryWStream decompressedDynamicMemoryWStream;
SkAssertResult(SkFlate::Inflate(compressed,
&decompressedDynamicMemoryWStream));
SkAutoTDelete<SkStreamAsset> decompressed(
decompressedDynamicMemoryWStream.detachAsStream());
SkAutoTDelete<SkStreamAsset> decompressed(stream_inflate(compressed));
if (decompressed->getLength() != size) {
ERRORF(r, "Decompression failed to get right size [%d]."

View File

@ -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<uint8_t> 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<double>(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);
}

View File

@ -99,12 +99,14 @@ static void TestPDFStream(skiatest::Reporter* reporter) {
SkAutoTUnref<SkPDFStream> 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("<</Filter /FlateDecode\n/Length 116>> 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);

View File

@ -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<SkData> 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);
}