Checking structure sizes before reading them from memory to avoid overflowing the buffer's stream.

BUG=
R=reed@google.com

Committed: https://code.google.com/p/skia/source/detail?r=12114

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

git-svn-id: http://skia.googlecode.com/svn/trunk@12119 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
sugoi@google.com 2013-11-04 20:28:23 +00:00
parent ac9d306a92
commit b48a59ae81
17 changed files with 334 additions and 192 deletions

View File

@ -559,9 +559,16 @@ public:
kMaxFlattenSize = 9 * sizeof(SkScalar) + sizeof(uint32_t)
};
// return the number of bytes written, whether or not buffer is null
uint32_t writeToMemory(void* buffer) const;
// return the number of bytes read
uint32_t readFromMemory(const void* buffer);
size_t writeToMemory(void* buffer) const;
/**
* Reads data from the buffer parameter
*
* @param buffer Memory to read from
* @param length Amount of memory available in the buffer
* @return number of bytes read (must be a multiple of 4) or
* 0 if there was not enough memory available
*/
size_t readFromMemory(const void* buffer, size_t length);
SkDEVCODE(void dump() const;)
SkDEVCODE(void toString(SkString*) const;)

View File

@ -899,16 +899,19 @@ public:
void dump() const;
/**
* Write the region to the buffer, and return the number of bytes written.
* Write the path to the buffer, and return the number of bytes written.
* If buffer is NULL, it still returns the number of bytes.
*/
uint32_t writeToMemory(void* buffer) const;
size_t writeToMemory(void* buffer) const;
/**
* Initialized the region from the buffer, returning the number
* of bytes actually read.
* Initializes the path from the buffer
*
* @param buffer Memory to read from
* @param length Amount of memory available in the buffer
* @return number of bytes read (must be a multiple of 4) or
* 0 if there was not enough memory available
*/
uint32_t readFromMemory(const void* buffer);
size_t readFromMemory(const void* buffer, size_t length);
/** Returns a non-zero, globally unique value corresponding to the set of verbs
and points in the path (but not the fill type [except on Android skbug.com/1762]).

View File

@ -244,14 +244,20 @@ public:
* write kSizeInMemory bytes, and that value is guaranteed to always be
* a multiple of 4. Return kSizeInMemory.
*/
uint32_t writeToMemory(void* buffer) const;
size_t writeToMemory(void* buffer) const;
/**
* Read the rrect from the specified buffer. This is guaranteed to always
* read kSizeInMemory bytes, and that value is guaranteed to always be
* a multiple of 4. Return kSizeInMemory.
* Reads the rrect from the specified buffer
*
* If the specified buffer is large enough, this will read kSizeInMemory bytes,
* and that value is guaranteed to always be a multiple of 4.
*
* @param buffer Memory to read from
* @param length Amount of memory available in the buffer
* @return number of bytes read (must be a multiple of 4) or
* 0 if there was not enough memory available
*/
uint32_t readFromMemory(const void* buffer);
size_t readFromMemory(const void* buffer, size_t length);
private:
SkRect fRect;

View File

@ -106,27 +106,20 @@ public:
int32_t readS32() { return this->readInt(); }
uint32_t readU32() { return this->readInt(); }
void readPath(SkPath* path) {
size_t size = path->readFromMemory(this->peek());
SkASSERT(SkAlign4(size) == size);
(void)this->skip(size);
bool readPath(SkPath* path) {
return readObjectFromMemory(path);
}
void readMatrix(SkMatrix* matrix) {
size_t size = matrix->readFromMemory(this->peek());
SkASSERT(SkAlign4(size) == size);
(void)this->skip(size);
bool readMatrix(SkMatrix* matrix) {
return readObjectFromMemory(matrix);
}
SkRRect* readRRect(SkRRect* rrect) {
rrect->readFromMemory(this->skip(SkRRect::kSizeInMemory));
return rrect;
bool readRRect(SkRRect* rrect) {
return readObjectFromMemory(rrect);
}
void readRegion(SkRegion* rgn) {
size_t size = rgn->readFromMemory(this->peek());
SkASSERT(SkAlign4(size) == size);
(void)this->skip(size);
bool readRegion(SkRegion* rgn) {
return readObjectFromMemory(rgn);
}
/**
@ -143,6 +136,15 @@ public:
size_t readIntoString(SkString* copy);
private:
template <typename T> bool readObjectFromMemory(T* obj) {
size_t size = obj->readFromMemory(this->peek(), this->available());
// If readFromMemory() fails (which means that available() was too small), it returns 0
bool success = (size > 0) && (size <= this->available()) && (SkAlign4(size) == size);
// In case of failure, we want to skip to the end
(void)this->skip(success ? size : this->available());
return success;
}
// these are always 4-byte aligned
const char* fCurr; // current position within buffer
const char* fStop; // end of buffer

View File

@ -361,13 +361,16 @@ public:
* Write the region to the buffer, and return the number of bytes written.
* If buffer is NULL, it still returns the number of bytes.
*/
uint32_t writeToMemory(void* buffer) const;
size_t writeToMemory(void* buffer) const;
/**
* Initialized the region from the buffer, returning the number
* of bytes actually read.
* Initializes the region from the buffer
*
* @param buffer Memory to read from
* @param length Amount of memory available in the buffer
* @return number of bytes read (must be a multiple of 4) or
* 0 if there was not enough memory available
*/
uint32_t readFromMemory(const void* buffer);
size_t readFromMemory(const void* buffer, size_t length);
/**
* Returns a reference to a global empty region. Just a convenience for

View File

@ -280,7 +280,7 @@ protected:
SkASSERT(size == size2);
SkRegion tmp3;
SkDEBUGCODE(size2 = ) tmp3.readFromMemory(buffer);
SkDEBUGCODE(size2 = ) tmp3.readFromMemory(buffer, 1000);
SkASSERT(size == size2);
SkASSERT(tmp3 == tmp);

View File

@ -34,6 +34,13 @@ size_t SkRBuffer::skipToAlign4()
return n;
}
void SkRBufferWithSizeCheck::read(void* buffer, size_t size) {
fError = fError || (fPos + size > fStop);
if (!fError && (size > 0)) {
readNoSizeCheck(buffer, size);
}
}
void* SkWBuffer::skip(size_t size)
{
void* result = fPos;

View File

@ -56,7 +56,7 @@ public:
/** Read the specified number of bytes from the data pointer. If buffer is not
null, copy those bytes into buffer.
*/
void read(void* buffer, size_t size) {
virtual void read(void* buffer, size_t size) {
if (size) {
this->readNoSizeCheck(buffer, size);
}
@ -74,7 +74,7 @@ public:
uint8_t readU8() { uint8_t x; read(&x, 1); return x; }
bool readBool() { return this->readU8() != 0; }
private:
protected:
void readNoSizeCheck(void* buffer, size_t size);
const char* fData;
@ -82,6 +82,28 @@ private:
const char* fStop;
};
/** \class SkRBufferWithSizeCheck
Same as SkRBuffer, except that a size check is performed before the read operation and an
error is set if the read operation is attempting to read past the end of the data.
*/
class SkRBufferWithSizeCheck : public SkRBuffer {
public:
SkRBufferWithSizeCheck(const void* data, size_t size) : SkRBuffer(data, size), fError(false) {}
/** Read the specified number of bytes from the data pointer. If buffer is not
null and the number of bytes to read does not overflow this object's data,
copy those bytes into buffer.
*/
virtual void read(void* buffer, size_t size) SK_OVERRIDE;
/** Returns whether or not a read operation attempted to read past the end of the data.
*/
bool isValid() const { return !fError; }
private:
bool fError;
};
/** \class SkWBuffer
Light weight class for writing data to a memory block.

View File

@ -1921,20 +1921,25 @@ const SkMatrix& SkMatrix::InvalidMatrix() {
///////////////////////////////////////////////////////////////////////////////
uint32_t SkMatrix::writeToMemory(void* buffer) const {
size_t SkMatrix::writeToMemory(void* buffer) const {
// TODO write less for simple matrices
static const size_t sizeInMemory = 9 * sizeof(SkScalar);
if (buffer) {
memcpy(buffer, fMat, 9 * sizeof(SkScalar));
memcpy(buffer, fMat, sizeInMemory);
}
return 9 * sizeof(SkScalar);
return sizeInMemory;
}
uint32_t SkMatrix::readFromMemory(const void* buffer) {
size_t SkMatrix::readFromMemory(const void* buffer, size_t length) {
static const size_t sizeInMemory = 9 * sizeof(SkScalar);
if (length < sizeInMemory) {
return 0;
}
if (buffer) {
memcpy(fMat, buffer, 9 * sizeof(SkScalar));
memcpy(fMat, buffer, sizeInMemory);
this->setTypeMask(kUnknown_Mask);
}
return 9 * sizeof(SkScalar);
return sizeInMemory;
}
#ifdef SK_DEVELOPER

View File

@ -2066,7 +2066,7 @@ SkPath::Verb SkPath::RawIter::next(SkPoint pts[4]) {
Format in compressed buffer: [ptCount, verbCount, pts[], verbs[]]
*/
uint32_t SkPath::writeToMemory(void* storage) const {
size_t SkPath::writeToMemory(void* storage) const {
SkDEBUGCODE(this->validate();)
if (NULL == storage) {
@ -2090,11 +2090,11 @@ uint32_t SkPath::writeToMemory(void* storage) const {
fPathRef->writeToBuffer(&buffer);
buffer.padToAlign4();
return SkToU32(buffer.pos());
return buffer.pos();
}
uint32_t SkPath::readFromMemory(const void* storage) {
SkRBuffer buffer(storage);
size_t SkPath::readFromMemory(const void* storage, size_t length) {
SkRBufferWithSizeCheck buffer(storage, length);
uint32_t packed = buffer.readS32();
fIsOval = (packed >> kIsOval_SerializationShift) & 1;
@ -2108,14 +2108,18 @@ uint32_t SkPath::readFromMemory(const void* storage) {
fPathRef.reset(SkPathRef::CreateFromBuffer(&buffer
#ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V14_AND_ALL_OTHER_INSTANCES_TOO
, newFormat, packed)
, newFormat, packed
#endif
);
));
buffer.skipToAlign4();
SkDEBUGCODE(this->validate();)
return SkToU32(buffer.pos());
size_t sizeRead = 0;
if (buffer.isValid()) {
SkDEBUGCODE(this->validate();)
sizeRead = buffer.pos();
}
return sizeRead;
}
///////////////////////////////////////////////////////////////////////////////

View File

@ -997,7 +997,8 @@ void SkPicturePlayback::draw(SkCanvas& canvas, SkDrawPictureCallback* callback)
case DRAW_RRECT: {
const SkPaint& paint = *getPaint(reader);
SkRRect rrect;
canvas.drawRRect(*reader.readRRect(&rrect), paint);
reader.readRRect(&rrect);
canvas.drawRRect(rrect, paint);
} break;
case DRAW_SPRITE: {
const SkPaint* paint = getPaint(reader);

View File

@ -259,7 +259,7 @@ void SkRRect::inset(SkScalar dx, SkScalar dy, SkRRect* dst) const {
///////////////////////////////////////////////////////////////////////////////
uint32_t SkRRect::writeToMemory(void* buffer) const {
size_t SkRRect::writeToMemory(void* buffer) const {
SkASSERT(kSizeInMemory == sizeof(SkRect) + sizeof(fRadii));
memcpy(buffer, &fRect, sizeof(SkRect));
@ -267,7 +267,11 @@ uint32_t SkRRect::writeToMemory(void* buffer) const {
return kSizeInMemory;
}
uint32_t SkRRect::readFromMemory(const void* buffer) {
size_t SkRRect::readFromMemory(const void* buffer, size_t length) {
if (length < kSizeInMemory) {
return 0;
}
SkScalar storage[12];
SkASSERT(sizeof(storage) == kSizeInMemory);

View File

@ -1100,9 +1100,9 @@ bool SkRegion::op(const SkRegion& rgna, const SkRegion& rgnb, Op op) {
#include "SkBuffer.h"
uint32_t SkRegion::writeToMemory(void* storage) const {
size_t SkRegion::writeToMemory(void* storage) const {
if (NULL == storage) {
uint32_t size = sizeof(int32_t); // -1 (empty), 0 (rect), runCount
size_t size = sizeof(int32_t); // -1 (empty), 0 (rect), runCount
if (!this->isEmpty()) {
size += sizeof(fBounds);
if (this->isComplex()) {
@ -1133,11 +1133,11 @@ uint32_t SkRegion::writeToMemory(void* storage) const {
return buffer.pos();
}
uint32_t SkRegion::readFromMemory(const void* storage) {
SkRBuffer buffer(storage);
SkRegion tmp;
int32_t count;
size_t SkRegion::readFromMemory(const void* storage, size_t length) {
SkRBufferWithSizeCheck buffer(storage, length);
SkRegion tmp;
int32_t count;
count = buffer.readS32();
if (count >= 0) {
buffer.read(&tmp.fBounds, sizeof(tmp.fBounds));
@ -1150,8 +1150,12 @@ uint32_t SkRegion::readFromMemory(const void* storage) {
buffer.read(tmp.fRunHead->writable_runs(), count * sizeof(RunType));
}
}
this->swap(tmp);
return buffer.pos();
size_t sizeRead = 0;
if (buffer.isValid()) {
this->swap(tmp);
sizeRead = buffer.pos();
}
return sizeRead;
}
///////////////////////////////////////////////////////////////////////////////

View File

@ -118,8 +118,11 @@ void SkValidatingReadBuffer::readPoint(SkPoint* point) {
}
void SkValidatingReadBuffer::readMatrix(SkMatrix* matrix) {
const size_t size = matrix->readFromMemory(fReader.peek());
this->validate(SkAlign4(size) == size);
size_t size = 0;
if (!fError) {
size = matrix->readFromMemory(fReader.peek(), fReader.available());
this->validate((SkAlign4(size) != size) || (0 == size));
}
if (!fError) {
(void)this->skip(size);
}
@ -140,16 +143,22 @@ void SkValidatingReadBuffer::readRect(SkRect* rect) {
}
void SkValidatingReadBuffer::readRegion(SkRegion* region) {
const size_t size = region->readFromMemory(fReader.peek());
this->validate(SkAlign4(size) == size);
size_t size = 0;
if (!fError) {
size = region->readFromMemory(fReader.peek(), fReader.available());
this->validate((SkAlign4(size) != size) || (0 == size));
}
if (!fError) {
(void)this->skip(size);
}
}
void SkValidatingReadBuffer::readPath(SkPath* path) {
const size_t size = path->readFromMemory(fReader.peek());
this->validate(SkAlign4(size) == size);
size_t size = 0;
if (!fError) {
size = path->readFromMemory(fReader.peek(), fReader.available());
this->validate((SkAlign4(size) != size) || (0 == size));
}
if (!fError) {
(void)this->skip(size);
}
@ -189,6 +198,8 @@ bool SkValidatingReadBuffer::readScalarArray(SkScalar* values, size_t size) {
}
uint32_t SkValidatingReadBuffer::getArrayCount() {
const size_t inc = sizeof(uint32_t);
fError = fError || !IsPtrAlign4(fReader.peek()) || !fReader.isAvailable(inc);
return *(uint32_t*)fReader.peek();
}

View File

@ -112,18 +112,19 @@ static void test_matrix_recttorect(skiatest::Reporter* reporter) {
static void test_flatten(skiatest::Reporter* reporter, const SkMatrix& m) {
// add 100 in case we have a bug, I don't want to kill my stack in the test
char buffer[SkMatrix::kMaxFlattenSize + 100];
uint32_t size1 = m.writeToMemory(NULL);
uint32_t size2 = m.writeToMemory(buffer);
static const size_t kBufferSize = SkMatrix::kMaxFlattenSize + 100;
char buffer[kBufferSize];
size_t size1 = m.writeToMemory(NULL);
size_t size2 = m.writeToMemory(buffer);
REPORTER_ASSERT(reporter, size1 == size2);
REPORTER_ASSERT(reporter, size1 <= SkMatrix::kMaxFlattenSize);
SkMatrix m2;
uint32_t size3 = m2.readFromMemory(buffer);
size_t size3 = m2.readFromMemory(buffer, kBufferSize);
REPORTER_ASSERT(reporter, size1 == size3);
REPORTER_ASSERT(reporter, are_equal(reporter, m, m2));
char buffer2[SkMatrix::kMaxFlattenSize + 100];
char buffer2[kBufferSize];
size3 = m2.writeToMemory(buffer2);
REPORTER_ASSERT(reporter, size1 == size3);
REPORTER_ASSERT(reporter, memcmp(buffer, buffer2, size1) == 0);

View File

@ -1808,12 +1808,12 @@ static void test_flattening(skiatest::Reporter* reporter) {
// create a buffer that should be much larger than the path so we don't
// kill our stack if writer goes too far.
char buffer[1024];
uint32_t size1 = p.writeToMemory(NULL);
uint32_t size2 = p.writeToMemory(buffer);
size_t size1 = p.writeToMemory(NULL);
size_t size2 = p.writeToMemory(buffer);
REPORTER_ASSERT(reporter, size1 == size2);
SkPath p2;
uint32_t size3 = p2.readFromMemory(buffer);
size_t size3 = p2.readFromMemory(buffer, 1024);
REPORTER_ASSERT(reporter, size1 == size3);
REPORTER_ASSERT(reporter, p == p2);

View File

@ -9,140 +9,202 @@
#include "SkValidatingReadBuffer.h"
#include "Test.h"
static void Tests(skiatest::Reporter* reporter) {
{
static const uint32_t arraySize = 512;
unsigned char data[arraySize] = {0};
SkOrderedWriteBuffer writer(1024);
writer.setFlags(SkOrderedWriteBuffer::kValidation_Flag);
static const uint32_t kArraySize = 64;
template<typename T>
static void TestAlignment(T* testObj, skiatest::Reporter* reporter) {
// Test memory read/write functions directly
unsigned char dataWritten[1024];
size_t bytesWrittenToMemory = testObj->writeToMemory(dataWritten);
REPORTER_ASSERT(reporter, SkAlign4(bytesWrittenToMemory) == bytesWrittenToMemory);
size_t bytesReadFromMemory = testObj->readFromMemory(dataWritten, bytesWrittenToMemory);
REPORTER_ASSERT(reporter, SkAlign4(bytesReadFromMemory) == bytesReadFromMemory);
}
template<typename T> struct SerializationUtils {
};
template<> struct SerializationUtils<SkMatrix> {
static void Write(SkOrderedWriteBuffer& writer, const SkMatrix* matrix) {
writer.writeMatrix(*matrix);
}
static void Read(SkValidatingReadBuffer& reader, SkMatrix* matrix) {
reader.readMatrix(matrix);
}
};
template<> struct SerializationUtils<SkPath> {
static void Write(SkOrderedWriteBuffer& writer, const SkPath* path) {
writer.writePath(*path);
}
static void Read(SkValidatingReadBuffer& reader, SkPath* path) {
reader.readPath(path);
}
};
template<> struct SerializationUtils<SkRegion> {
static void Write(SkOrderedWriteBuffer& writer, const SkRegion* region) {
writer.writeRegion(*region);
}
static void Read(SkValidatingReadBuffer& reader, SkRegion* region) {
reader.readRegion(region);
}
};
template<> struct SerializationUtils<unsigned char> {
static void Write(SkOrderedWriteBuffer& writer, unsigned char* data, uint32_t arraySize) {
writer.writeByteArray(data, arraySize);
uint32_t bytesWritten = writer.bytesWritten();
// This should write the length (in 4 bytes) and the array
REPORTER_ASSERT(reporter, (4 + arraySize) == bytesWritten);
unsigned char dataWritten[1024];
writer.writeToMemory(dataWritten);
// Make sure this fails when it should
SkValidatingReadBuffer buffer(dataWritten, bytesWritten);
unsigned char dataRead[arraySize];
bool success = buffer.readByteArray(dataRead, 256);
// This should have failed, since 256 < sizeInBytes
REPORTER_ASSERT(reporter, !success);
// Make sure this succeeds when it should
SkValidatingReadBuffer buffer2(dataWritten, bytesWritten);
success = buffer2.readByteArray(dataRead, arraySize);
// This should have succeeded, since there are enough bytes to read this
REPORTER_ASSERT(reporter, success);
}
static bool Read(SkValidatingReadBuffer& reader, unsigned char* data, uint32_t arraySize) {
return reader.readByteArray(data, arraySize);
}
};
{
static const uint32_t arraySize = 64;
SkColor data[arraySize];
SkOrderedWriteBuffer writer(1024);
writer.setFlags(SkOrderedWriteBuffer::kValidation_Flag);
template<> struct SerializationUtils<SkColor> {
static void Write(SkOrderedWriteBuffer& writer, SkColor* data, uint32_t arraySize) {
writer.writeColorArray(data, arraySize);
uint32_t bytesWritten = writer.bytesWritten();
// This should write the length (in 4 bytes) and the array
REPORTER_ASSERT(reporter, (4 + arraySize * sizeof(SkColor)) == bytesWritten);
unsigned char dataWritten[1024];
writer.writeToMemory(dataWritten);
// Make sure this fails when it should
SkValidatingReadBuffer buffer(dataWritten, bytesWritten);
SkColor dataRead[arraySize];
bool success = buffer.readColorArray(dataRead, 32);
// This should have failed, since 256 < sizeInBytes
REPORTER_ASSERT(reporter, !success);
// Make sure this succeeds when it should
SkValidatingReadBuffer buffer2(dataWritten, bytesWritten);
success = buffer2.readColorArray(dataRead, arraySize);
// This should have succeeded, since there are enough bytes to read this
REPORTER_ASSERT(reporter, success);
}
static bool Read(SkValidatingReadBuffer& reader, SkColor* data, uint32_t arraySize) {
return reader.readColorArray(data, arraySize);
}
};
{
static const uint32_t arraySize = 64;
int32_t data[arraySize];
SkOrderedWriteBuffer writer(1024);
writer.setFlags(SkOrderedWriteBuffer::kValidation_Flag);
template<> struct SerializationUtils<int32_t> {
static void Write(SkOrderedWriteBuffer& writer, int32_t* data, uint32_t arraySize) {
writer.writeIntArray(data, arraySize);
uint32_t bytesWritten = writer.bytesWritten();
// This should write the length (in 4 bytes) and the array
REPORTER_ASSERT(reporter, (4 + arraySize * sizeof(int32_t)) == bytesWritten);
unsigned char dataWritten[1024];
writer.writeToMemory(dataWritten);
// Make sure this fails when it should
SkValidatingReadBuffer buffer(dataWritten, bytesWritten);
int32_t dataRead[arraySize];
bool success = buffer.readIntArray(dataRead, 32);
// This should have failed, since 256 < sizeInBytes
REPORTER_ASSERT(reporter, !success);
// Make sure this succeeds when it should
SkValidatingReadBuffer buffer2(dataWritten, bytesWritten);
success = buffer2.readIntArray(dataRead, arraySize);
// This should have succeeded, since there are enough bytes to read this
REPORTER_ASSERT(reporter, success);
}
static bool Read(SkValidatingReadBuffer& reader, int32_t* data, uint32_t arraySize) {
return reader.readIntArray(data, arraySize);
}
};
{
static const uint32_t arraySize = 64;
SkPoint data[arraySize];
SkOrderedWriteBuffer writer(1024);
writer.setFlags(SkOrderedWriteBuffer::kValidation_Flag);
template<> struct SerializationUtils<SkPoint> {
static void Write(SkOrderedWriteBuffer& writer, SkPoint* data, uint32_t arraySize) {
writer.writePointArray(data, arraySize);
uint32_t bytesWritten = writer.bytesWritten();
// This should write the length (in 4 bytes) and the array
REPORTER_ASSERT(reporter, (4 + arraySize * sizeof(SkPoint)) == bytesWritten);
}
static bool Read(SkValidatingReadBuffer& reader, SkPoint* data, uint32_t arraySize) {
return reader.readPointArray(data, arraySize);
}
};
unsigned char dataWritten[1024];
writer.writeToMemory(dataWritten);
template<> struct SerializationUtils<SkScalar> {
static void Write(SkOrderedWriteBuffer& writer, SkScalar* data, uint32_t arraySize) {
writer.writeScalarArray(data, arraySize);
}
static bool Read(SkValidatingReadBuffer& reader, SkScalar* data, uint32_t arraySize) {
return reader.readScalarArray(data, arraySize);
}
};
// Make sure this fails when it should
SkValidatingReadBuffer buffer(dataWritten, bytesWritten);
SkPoint dataRead[arraySize];
bool success = buffer.readPointArray(dataRead, 32);
// This should have failed, since 256 < sizeInBytes
REPORTER_ASSERT(reporter, !success);
template<typename T>
static void TestObjectSerialization(T* testObj, skiatest::Reporter* reporter) {
SkOrderedWriteBuffer writer(1024);
writer.setFlags(SkOrderedWriteBuffer::kValidation_Flag);
SerializationUtils<T>::Write(writer, testObj);
size_t bytesWritten = writer.bytesWritten();
REPORTER_ASSERT(reporter, SkAlign4(bytesWritten) == bytesWritten);
// Make sure this succeeds when it should
SkValidatingReadBuffer buffer2(dataWritten, bytesWritten);
success = buffer2.readPointArray(dataRead, arraySize);
// This should have succeeded, since there are enough bytes to read this
REPORTER_ASSERT(reporter, success);
unsigned char dataWritten[1024];
writer.writeToMemory(dataWritten);
// Make sure this fails when it should (test with smaller size, but still multiple of 4)
SkValidatingReadBuffer buffer(dataWritten, bytesWritten - 4);
const unsigned char* peekBefore = static_cast<const unsigned char*>(buffer.skip(0));
SerializationUtils<T>::Read(buffer, testObj);
const unsigned char* peekAfter = static_cast<const unsigned char*>(buffer.skip(0));
// This should have failed, since the buffer is too small to read a matrix from it
REPORTER_ASSERT(reporter, peekBefore == peekAfter);
// Make sure this succeeds when it should
SkValidatingReadBuffer buffer2(dataWritten, bytesWritten);
peekBefore = static_cast<const unsigned char*>(buffer2.skip(0));
SerializationUtils<T>::Read(buffer2, testObj);
peekAfter = static_cast<const unsigned char*>(buffer2.skip(0));
// This should have succeeded, since there are enough bytes to read this
REPORTER_ASSERT(reporter, static_cast<size_t>(peekAfter - peekBefore) == bytesWritten);
TestAlignment(testObj, reporter);
}
template<typename T>
static void TestArraySerialization(T* data, skiatest::Reporter* reporter) {
SkOrderedWriteBuffer writer(1024);
writer.setFlags(SkOrderedWriteBuffer::kValidation_Flag);
SerializationUtils<T>::Write(writer, data, kArraySize);
size_t bytesWritten = writer.bytesWritten();
// This should write the length (in 4 bytes) and the array
REPORTER_ASSERT(reporter, (4 + kArraySize * sizeof(T)) == bytesWritten);
unsigned char dataWritten[1024];
writer.writeToMemory(dataWritten);
// Make sure this fails when it should
SkValidatingReadBuffer buffer(dataWritten, bytesWritten);
T dataRead[kArraySize];
bool success = SerializationUtils<T>::Read(buffer, dataRead, kArraySize / 2);
// This should have failed, since the provided size was too small
REPORTER_ASSERT(reporter, !success);
// Make sure this succeeds when it should
SkValidatingReadBuffer buffer2(dataWritten, bytesWritten);
success = SerializationUtils<T>::Read(buffer2, dataRead, kArraySize);
// This should have succeeded, since there are enough bytes to read this
REPORTER_ASSERT(reporter, success);
}
static void Tests(skiatest::Reporter* reporter) {
// Test matrix serialization
{
SkMatrix matrix = SkMatrix::I();
TestObjectSerialization(&matrix, reporter);
}
// Test path serialization
{
SkPath path;
TestObjectSerialization(&path, reporter);
}
// Test region serialization
{
static const uint32_t arraySize = 64;
SkScalar data[arraySize];
SkOrderedWriteBuffer writer(1024);
writer.setFlags(SkOrderedWriteBuffer::kValidation_Flag);
writer.writeScalarArray(data, arraySize);
uint32_t bytesWritten = writer.bytesWritten();
// This should write the length (in 4 bytes) and the array
REPORTER_ASSERT(reporter, (4 + arraySize * sizeof(SkScalar)) == bytesWritten);
SkRegion region;
TestObjectSerialization(&region, reporter);
}
unsigned char dataWritten[1024];
writer.writeToMemory(dataWritten);
// Test rrect serialization
{
SkRRect rrect;
TestAlignment(&rrect, reporter);
}
// Make sure this fails when it should
SkValidatingReadBuffer buffer(dataWritten, bytesWritten);
SkScalar dataRead[arraySize];
bool success = buffer.readScalarArray(dataRead, 32);
// This should have failed, since 256 < sizeInBytes
REPORTER_ASSERT(reporter, !success);
// Test readByteArray
{
unsigned char data[kArraySize] = {0};
TestArraySerialization(data, reporter);
}
// Make sure this succeeds when it should
SkValidatingReadBuffer buffer2(dataWritten, bytesWritten);
success = buffer2.readScalarArray(dataRead, arraySize);
// This should have succeeded, since there are enough bytes to read this
REPORTER_ASSERT(reporter, success);
// Test readColorArray
{
SkColor data[kArraySize];
TestArraySerialization(data, reporter);
}
// Test readIntArray
{
int32_t data[kArraySize];
TestArraySerialization(data, reporter);
}
// Test readPointArray
{
SkPoint data[kArraySize];
TestArraySerialization(data, reporter);
}
// Test readScalarArray
{
SkScalar data[kArraySize];
TestArraySerialization(data, reporter);
}
}