Home | History | Annotate | Download | only in core
      1 
      2 /*
      3  * Copyright 2006 The Android Open Source Project
      4  *
      5  * Use of this source code is governed by a BSD-style license that can be
      6  * found in the LICENSE file.
      7  */
      8 
      9 
     10 #include "SkStream.h"
     11 #include "SkData.h"
     12 #include "SkFixed.h"
     13 #include "SkString.h"
     14 #include "SkOSFile.h"
     15 
     16 SK_DEFINE_INST_COUNT(SkStream)
     17 SK_DEFINE_INST_COUNT(SkWStream)
     18 SK_DEFINE_INST_COUNT(SkFILEStream)
     19 SK_DEFINE_INST_COUNT(SkFDStream)
     20 SK_DEFINE_INST_COUNT(SkMemoryStream)
     21 SK_DEFINE_INST_COUNT(SkBufferStream)
     22 SK_DEFINE_INST_COUNT(SkFILEWStream)
     23 SK_DEFINE_INST_COUNT(SkMemoryWStream)
     24 SK_DEFINE_INST_COUNT(SkDynamicMemoryWStream)
     25 SK_DEFINE_INST_COUNT(SkDebugWStream)
     26 
     27 ///////////////////////////////////////////////////////////////////////////////
     28 
     29 const char* SkStream::getFileName()
     30 {
     31     // override in subclass if you represent a file
     32     return NULL;
     33 }
     34 
     35 const void* SkStream::getMemoryBase()
     36 {
     37     // override in subclass if you represent a memory block
     38     return NULL;
     39 }
     40 
     41 size_t SkStream::skip(size_t size)
     42 {
     43     /*  Check for size == 0, and just return 0. If we passed that
     44         to read(), it would interpret it as a request for the entire
     45         size of the stream.
     46     */
     47     return size ? this->read(NULL, size) : 0;
     48 }
     49 
     50 int8_t SkStream::readS8() {
     51     int8_t value;
     52     SkDEBUGCODE(size_t len =) this->read(&value, 1);
     53     SkASSERT(1 == len);
     54     return value;
     55 }
     56 
     57 int16_t SkStream::readS16() {
     58     int16_t value;
     59     SkDEBUGCODE(size_t len =) this->read(&value, 2);
     60     SkASSERT(2 == len);
     61     return value;
     62 }
     63 
     64 int32_t SkStream::readS32() {
     65     int32_t value;
     66     SkDEBUGCODE(size_t len =) this->read(&value, 4);
     67     SkASSERT(4 == len);
     68     return value;
     69 }
     70 
     71 SkScalar SkStream::readScalar() {
     72     SkScalar value;
     73     SkDEBUGCODE(size_t len =) this->read(&value, sizeof(SkScalar));
     74     SkASSERT(sizeof(SkScalar) == len);
     75     return value;
     76 }
     77 
     78 #define SK_MAX_BYTE_FOR_U8          0xFD
     79 #define SK_BYTE_SENTINEL_FOR_U16    0xFE
     80 #define SK_BYTE_SENTINEL_FOR_U32    0xFF
     81 
     82 size_t SkStream::readPackedUInt() {
     83     uint8_t byte;
     84     if (!this->read(&byte, 1)) {
     85         return 0;
     86     }
     87     if (SK_BYTE_SENTINEL_FOR_U16 == byte) {
     88         return this->readU16();
     89     } else if (SK_BYTE_SENTINEL_FOR_U32 == byte) {
     90         return this->readU32();
     91     } else {
     92         return byte;
     93     }
     94 }
     95 
     96 SkData* SkStream::readData() {
     97     size_t size = this->readU32();
     98     if (0 == size) {
     99         return SkData::NewEmpty();
    100     } else {
    101         void* buffer = sk_malloc_throw(size);
    102         this->read(buffer, size);
    103         return SkData::NewFromMalloc(buffer, size);
    104     }
    105 }
    106 
    107 //////////////////////////////////////////////////////////////////////////////////////
    108 
    109 SkWStream::~SkWStream()
    110 {
    111 }
    112 
    113 void SkWStream::newline()
    114 {
    115     this->write("\n", 1);
    116 }
    117 
    118 void SkWStream::flush()
    119 {
    120 }
    121 
    122 bool SkWStream::writeText(const char text[])
    123 {
    124     SkASSERT(text);
    125     return this->write(text, strlen(text));
    126 }
    127 
    128 bool SkWStream::writeDecAsText(int32_t dec)
    129 {
    130     SkString    tmp;
    131     tmp.appendS32(dec);
    132     return this->write(tmp.c_str(), tmp.size());
    133 }
    134 
    135 bool SkWStream::writeBigDecAsText(int64_t dec, int minDigits)
    136 {
    137     SkString    tmp;
    138     tmp.appendS64(dec, minDigits);
    139     return this->write(tmp.c_str(), tmp.size());
    140 }
    141 
    142 bool SkWStream::writeHexAsText(uint32_t hex, int digits)
    143 {
    144     SkString    tmp;
    145     tmp.appendHex(hex, digits);
    146     return this->write(tmp.c_str(), tmp.size());
    147 }
    148 
    149 bool SkWStream::writeScalarAsText(SkScalar value)
    150 {
    151     SkString    tmp;
    152     tmp.appendScalar(value);
    153     return this->write(tmp.c_str(), tmp.size());
    154 }
    155 
    156 bool SkWStream::write8(U8CPU value) {
    157     uint8_t v = SkToU8(value);
    158     return this->write(&v, 1);
    159 }
    160 
    161 bool SkWStream::write16(U16CPU value) {
    162     uint16_t v = SkToU16(value);
    163     return this->write(&v, 2);
    164 }
    165 
    166 bool SkWStream::write32(uint32_t value) {
    167     return this->write(&value, 4);
    168 }
    169 
    170 bool SkWStream::writeScalar(SkScalar value) {
    171     return this->write(&value, sizeof(value));
    172 }
    173 
    174 bool SkWStream::writePackedUInt(size_t value) {
    175     uint8_t data[5];
    176     size_t len = 1;
    177     if (value <= SK_MAX_BYTE_FOR_U8) {
    178         data[0] = value;
    179         len = 1;
    180     } else if (value <= 0xFFFF) {
    181         uint16_t value16 = value;
    182         data[0] = SK_BYTE_SENTINEL_FOR_U16;
    183         memcpy(&data[1], &value16, 2);
    184         len = 3;
    185     } else {
    186         uint32_t value32 = value;
    187         data[0] = SK_BYTE_SENTINEL_FOR_U32;
    188         memcpy(&data[1], &value32, 4);
    189         len = 5;
    190     }
    191     return this->write(data, len);
    192 }
    193 
    194 bool SkWStream::writeStream(SkStream* stream, size_t length) {
    195     char scratch[1024];
    196     const size_t MAX = sizeof(scratch);
    197 
    198     while (length != 0) {
    199         size_t n = length;
    200         if (n > MAX) {
    201             n = MAX;
    202         }
    203         stream->read(scratch, n);
    204         if (!this->write(scratch, n)) {
    205             return false;
    206         }
    207         length -= n;
    208     }
    209     return true;
    210 }
    211 
    212 bool SkWStream::writeData(const SkData* data) {
    213     if (data) {
    214         this->write32(data->size());
    215         this->write(data->data(), data->size());
    216     } else {
    217         this->write32(0);
    218     }
    219     return true;
    220 }
    221 
    222 ///////////////////////////////////////////////////////////////////////////////
    223 
    224 SkFILEStream::SkFILEStream(const char file[]) : fName(file)
    225 {
    226     fFILE = file ? sk_fopen(fName.c_str(), kRead_SkFILE_Flag) : NULL;
    227 }
    228 
    229 SkFILEStream::~SkFILEStream()
    230 {
    231     if (fFILE)
    232         sk_fclose(fFILE);
    233 }
    234 
    235 void SkFILEStream::setPath(const char path[])
    236 {
    237     fName.set(path);
    238     if (fFILE)
    239     {
    240         sk_fclose(fFILE);
    241         fFILE = NULL;
    242     }
    243     if (path)
    244         fFILE = sk_fopen(fName.c_str(), kRead_SkFILE_Flag);
    245 }
    246 
    247 const char* SkFILEStream::getFileName()
    248 {
    249     return fName.c_str();
    250 }
    251 
    252 bool SkFILEStream::rewind()
    253 {
    254     if (fFILE)
    255     {
    256         if (sk_frewind(fFILE))
    257             return true;
    258         // we hit an error
    259         sk_fclose(fFILE);
    260         fFILE = NULL;
    261     }
    262     return false;
    263 }
    264 
    265 size_t SkFILEStream::read(void* buffer, size_t size)
    266 {
    267     if (fFILE)
    268     {
    269         if (buffer == NULL && size == 0)    // special signature, they want the total size
    270             return sk_fgetsize(fFILE);
    271         else
    272             return sk_fread(buffer, size, fFILE);
    273     }
    274     return 0;
    275 }
    276 
    277 ///////////////////////////////////////////////////////////////////////////////
    278 
    279 static SkData* newFromParams(const void* src, size_t size, bool copyData) {
    280     if (copyData) {
    281         return SkData::NewWithCopy(src, size);
    282     } else {
    283         return SkData::NewWithProc(src, size, NULL, NULL);
    284     }
    285 }
    286 
    287 SkMemoryStream::SkMemoryStream() {
    288     fData = SkData::NewEmpty();
    289     fOffset = 0;
    290 }
    291 
    292 SkMemoryStream::SkMemoryStream(size_t size) {
    293     fData = SkData::NewFromMalloc(sk_malloc_throw(size), size);
    294     fOffset = 0;
    295 }
    296 
    297 SkMemoryStream::SkMemoryStream(const void* src, size_t size, bool copyData) {
    298     fData = newFromParams(src, size, copyData);
    299     fOffset = 0;
    300 }
    301 
    302 SkMemoryStream::SkMemoryStream(SkData* data) {
    303     if (NULL == data) {
    304         fData = SkData::NewEmpty();
    305     } else {
    306         fData = data;
    307         fData->ref();
    308     }
    309     fOffset = 0;
    310 }
    311 
    312 SkMemoryStream::~SkMemoryStream() {
    313     fData->unref();
    314 }
    315 
    316 void SkMemoryStream::setMemoryOwned(const void* src, size_t size) {
    317     fData->unref();
    318     fData = SkData::NewFromMalloc(src, size);
    319     fOffset = 0;
    320 }
    321 
    322 void SkMemoryStream::setMemory(const void* src, size_t size, bool copyData) {
    323     fData->unref();
    324     fData = newFromParams(src, size, copyData);
    325     fOffset = 0;
    326 }
    327 
    328 SkData* SkMemoryStream::copyToData() const {
    329     fData->ref();
    330     return fData;
    331 }
    332 
    333 SkData* SkMemoryStream::setData(SkData* data) {
    334     fData->unref();
    335     if (NULL == data) {
    336         fData = SkData::NewEmpty();
    337     } else {
    338         fData = data;
    339         fData->ref();
    340     }
    341     return data;
    342 }
    343 
    344 void SkMemoryStream::skipToAlign4() {
    345     // cast to remove unary-minus warning
    346     fOffset += -(int)fOffset & 0x03;
    347 }
    348 
    349 bool SkMemoryStream::rewind() {
    350     fOffset = 0;
    351     return true;
    352 }
    353 
    354 size_t SkMemoryStream::read(void* buffer, size_t size) {
    355     size_t dataSize = fData->size();
    356 
    357     if (buffer == NULL && size == 0)    // special signature, they want the total size
    358         return dataSize;
    359 
    360     // if buffer is NULL, seek ahead by size
    361 
    362     if (size == 0) {
    363         return 0;
    364     }
    365     if (size > dataSize - fOffset) {
    366         size = dataSize - fOffset;
    367     }
    368     if (buffer) {
    369         memcpy(buffer, fData->bytes() + fOffset, size);
    370     }
    371     fOffset += size;
    372     return size;
    373 }
    374 
    375 const void* SkMemoryStream::getMemoryBase() {
    376     return fData->data();
    377 }
    378 
    379 const void* SkMemoryStream::getAtPos() {
    380     return fData->bytes() + fOffset;
    381 }
    382 
    383 size_t SkMemoryStream::seek(size_t offset) {
    384     if (offset > fData->size()) {
    385         offset = fData->size();
    386     }
    387     fOffset = offset;
    388     return offset;
    389 }
    390 
    391 ///////////////////////////////////////////////////////////////////////////////
    392 
    393 SkBufferStream::SkBufferStream(SkStream* proxy, size_t bufferSize)
    394     : fProxy(proxy)
    395 {
    396     SkASSERT(proxy != NULL);
    397     proxy->ref();
    398     this->init(NULL, bufferSize);
    399 }
    400 
    401 SkBufferStream::SkBufferStream(SkStream* proxy, void* buffer, size_t bufferSize)
    402     : fProxy(proxy)
    403 {
    404     SkASSERT(proxy != NULL);
    405     SkASSERT(buffer == NULL || bufferSize != 0);    // init(addr, 0) makes no sense, we must know how big their buffer is
    406     proxy->ref();
    407     this->init(buffer, bufferSize);
    408 }
    409 
    410 void SkBufferStream::init(void* buffer, size_t bufferSize)
    411 {
    412     if (bufferSize == 0)
    413         bufferSize = kDefaultBufferSize;
    414 
    415     fOrigBufferSize = bufferSize;
    416     fBufferSize = bufferSize;
    417     fBufferOffset = bufferSize; // to trigger a reload on the first read()
    418 
    419     if (buffer == NULL)
    420     {
    421         fBuffer = (char*)sk_malloc_throw(fBufferSize);
    422         fWeOwnTheBuffer = true;
    423     }
    424     else
    425     {
    426         fBuffer = (char*)buffer;
    427         fWeOwnTheBuffer = false;
    428     }
    429 }
    430 
    431 SkBufferStream::~SkBufferStream()
    432 {
    433     fProxy->unref();
    434     if (fWeOwnTheBuffer)
    435         sk_free(fBuffer);
    436 }
    437 
    438 bool SkBufferStream::rewind()
    439 {
    440     fBufferOffset = fBufferSize = fOrigBufferSize;
    441     return fProxy->rewind();
    442 }
    443 
    444 const char* SkBufferStream::getFileName()
    445 {
    446     return fProxy->getFileName();
    447 }
    448 
    449 #ifdef SK_DEBUG
    450 //  #define SK_TRACE_BUFFERSTREAM
    451 #endif
    452 
    453 size_t SkBufferStream::read(void* buffer, size_t size) {
    454 #ifdef SK_TRACE_BUFFERSTREAM
    455     SkDebugf("Request %d", size);
    456 #endif
    457 
    458     if (buffer == NULL && size == 0) {
    459         return fProxy->read(buffer, size);    // requesting total size
    460     }
    461 
    462     if (0 == size) {
    463         return 0;
    464     }
    465 
    466     // skip size bytes
    467     if (NULL == buffer) {
    468         size_t remaining = fBufferSize - fBufferOffset;
    469         if (remaining >= size) {
    470             fBufferOffset += size;
    471             return size;
    472         }
    473         // if we get here, we are being asked to skip beyond our current buffer
    474         // so reset our offset to force a read next time, and skip the diff
    475         // in our proxy
    476         fBufferOffset = fOrigBufferSize;
    477         return remaining + fProxy->read(NULL, size - remaining);
    478     }
    479 
    480     size_t s = size;
    481     size_t actuallyRead = 0;
    482 
    483     // flush what we can from our fBuffer
    484     if (fBufferOffset < fBufferSize)
    485     {
    486         if (s > fBufferSize - fBufferOffset)
    487             s = fBufferSize - fBufferOffset;
    488         memcpy(buffer, fBuffer + fBufferOffset, s);
    489 #ifdef SK_TRACE_BUFFERSTREAM
    490         SkDebugf(" flush %d", s);
    491 #endif
    492         size -= s;
    493         fBufferOffset += s;
    494         buffer = (char*)buffer + s;
    495         actuallyRead = s;
    496     }
    497 
    498     // check if there is more to read
    499     if (size)
    500     {
    501         SkASSERT(fBufferOffset >= fBufferSize); // need to refill our fBuffer
    502 
    503         if (size < fBufferSize) // lets try to read more than the request
    504         {
    505             s = fProxy->read(fBuffer, fBufferSize);
    506 #ifdef SK_TRACE_BUFFERSTREAM
    507             SkDebugf(" read %d into fBuffer", s);
    508 #endif
    509             if (size > s)   // they asked for too much
    510                 size = s;
    511             if (size)
    512             {
    513                 memcpy(buffer, fBuffer, size);
    514                 actuallyRead += size;
    515 #ifdef SK_TRACE_BUFFERSTREAM
    516                 SkDebugf(" memcpy %d into dst", size);
    517 #endif
    518             }
    519 
    520             fBufferOffset = size;
    521             fBufferSize = s;        // record the (possibly smaller) size for the buffer
    522         }
    523         else    // just do a direct read
    524         {
    525             actuallyRead += fProxy->read(buffer, size);
    526 #ifdef SK_TRACE_BUFFERSTREAM
    527             SkDebugf(" direct read %d", size);
    528 #endif
    529         }
    530     }
    531 #ifdef SK_TRACE_BUFFERSTREAM
    532     SkDebugf("\n");
    533 #endif
    534     return actuallyRead;
    535 }
    536 
    537 const void* SkBufferStream::getMemoryBase()
    538 {
    539     return fProxy->getMemoryBase();
    540 }
    541 
    542 /////////////////////////////////////////////////////////////////////////////////////////////////////////
    543 /////////////////////////////////////////////////////////////////////////////////////////////////////////
    544 
    545 SkFILEWStream::SkFILEWStream(const char path[])
    546 {
    547     fFILE = sk_fopen(path, kWrite_SkFILE_Flag);
    548 }
    549 
    550 SkFILEWStream::~SkFILEWStream()
    551 {
    552     if (fFILE)
    553         sk_fclose(fFILE);
    554 }
    555 
    556 bool SkFILEWStream::write(const void* buffer, size_t size)
    557 {
    558     if (fFILE == NULL)
    559         return false;
    560 
    561     if (sk_fwrite(buffer, size, fFILE) != size)
    562     {
    563         SkDEBUGCODE(SkDebugf("SkFILEWStream failed writing %d bytes\n", size);)
    564         sk_fclose(fFILE);
    565         fFILE = NULL;
    566         return false;
    567     }
    568     return true;
    569 }
    570 
    571 void SkFILEWStream::flush()
    572 {
    573     if (fFILE)
    574         sk_fflush(fFILE);
    575 }
    576 
    577 ////////////////////////////////////////////////////////////////////////
    578 
    579 SkMemoryWStream::SkMemoryWStream(void* buffer, size_t size)
    580     : fBuffer((char*)buffer), fMaxLength(size), fBytesWritten(0)
    581 {
    582 }
    583 
    584 bool SkMemoryWStream::write(const void* buffer, size_t size)
    585 {
    586     size = SkMin32(size, fMaxLength - fBytesWritten);
    587     if (size > 0)
    588     {
    589         memcpy(fBuffer + fBytesWritten, buffer, size);
    590         fBytesWritten += size;
    591         return true;
    592     }
    593     return false;
    594 }
    595 
    596 ////////////////////////////////////////////////////////////////////////
    597 
    598 #define SkDynamicMemoryWStream_MinBlockSize   256
    599 
    600 struct SkDynamicMemoryWStream::Block {
    601     Block*  fNext;
    602     char*   fCurr;
    603     char*   fStop;
    604 
    605     const char* start() const { return (const char*)(this + 1); }
    606     char*   start() { return (char*)(this + 1); }
    607     size_t  avail() const { return fStop - fCurr; }
    608     size_t  written() const { return fCurr - this->start(); }
    609 
    610     void init(size_t size)
    611     {
    612         fNext = NULL;
    613         fCurr = this->start();
    614         fStop = this->start() + size;
    615     }
    616 
    617     const void* append(const void* data, size_t size)
    618     {
    619         SkASSERT((size_t)(fStop - fCurr) >= size);
    620         memcpy(fCurr, data, size);
    621         fCurr += size;
    622         return (const void*)((const char*)data + size);
    623     }
    624 };
    625 
    626 SkDynamicMemoryWStream::SkDynamicMemoryWStream()
    627     : fHead(NULL), fTail(NULL), fBytesWritten(0), fCopy(NULL)
    628 {
    629 }
    630 
    631 SkDynamicMemoryWStream::~SkDynamicMemoryWStream()
    632 {
    633     reset();
    634 }
    635 
    636 void SkDynamicMemoryWStream::reset()
    637 {
    638     this->invalidateCopy();
    639 
    640     Block*  block = fHead;
    641 
    642     while (block != NULL) {
    643         Block*  next = block->fNext;
    644         sk_free(block);
    645         block = next;
    646     }
    647     fHead = fTail = NULL;
    648     fBytesWritten = 0;
    649 }
    650 
    651 bool SkDynamicMemoryWStream::write(const void* buffer, size_t count)
    652 {
    653     if (count > 0) {
    654         this->invalidateCopy();
    655 
    656         fBytesWritten += count;
    657 
    658         size_t  size;
    659 
    660         if (fTail != NULL && fTail->avail() > 0) {
    661             size = SkMin32(fTail->avail(), count);
    662             buffer = fTail->append(buffer, size);
    663             SkASSERT(count >= size);
    664             count -= size;
    665             if (count == 0)
    666                 return true;
    667         }
    668 
    669         size = SkMax32(count, SkDynamicMemoryWStream_MinBlockSize);
    670         Block* block = (Block*)sk_malloc_throw(sizeof(Block) + size);
    671         block->init(size);
    672         block->append(buffer, count);
    673 
    674         if (fTail != NULL)
    675             fTail->fNext = block;
    676         else
    677             fHead = fTail = block;
    678         fTail = block;
    679     }
    680     return true;
    681 }
    682 
    683 bool SkDynamicMemoryWStream::write(const void* buffer, size_t offset, size_t count)
    684 {
    685     if (offset + count > fBytesWritten) {
    686         return false; // test does not partially modify
    687     }
    688 
    689     this->invalidateCopy();
    690 
    691     Block* block = fHead;
    692     while (block != NULL) {
    693         size_t size = block->written();
    694         if (offset < size) {
    695             size_t part = offset + count > size ? size - offset : count;
    696             memcpy(block->start() + offset, buffer, part);
    697             if (count <= part)
    698                 return true;
    699             count -= part;
    700             buffer = (const void*) ((char* ) buffer + part);
    701         }
    702         offset = offset > size ? offset - size : 0;
    703         block = block->fNext;
    704     }
    705     return false;
    706 }
    707 
    708 bool SkDynamicMemoryWStream::read(void* buffer, size_t offset, size_t count)
    709 {
    710     if (offset + count > fBytesWritten)
    711         return false; // test does not partially modify
    712     Block* block = fHead;
    713     while (block != NULL) {
    714         size_t size = block->written();
    715         if (offset < size) {
    716             size_t part = offset + count > size ? size - offset : count;
    717             memcpy(buffer, block->start() + offset, part);
    718             if (count <= part)
    719                 return true;
    720             count -= part;
    721             buffer = (void*) ((char* ) buffer + part);
    722         }
    723         offset = offset > size ? offset - size : 0;
    724         block = block->fNext;
    725     }
    726     return false;
    727 }
    728 
    729 void SkDynamicMemoryWStream::copyTo(void* dst) const
    730 {
    731     if (fCopy) {
    732         memcpy(dst, fCopy->data(), fBytesWritten);
    733     } else {
    734         Block* block = fHead;
    735 
    736         while (block != NULL) {
    737             size_t size = block->written();
    738             memcpy(dst, block->start(), size);
    739             dst = (void*)((char*)dst + size);
    740             block = block->fNext;
    741         }
    742     }
    743 }
    744 
    745 void SkDynamicMemoryWStream::padToAlign4()
    746 {
    747     // cast to remove unary-minus warning
    748     int padBytes = -(int)fBytesWritten & 0x03;
    749     if (padBytes == 0)
    750         return;
    751     int zero = 0;
    752     write(&zero, padBytes);
    753 }
    754 
    755 SkData* SkDynamicMemoryWStream::copyToData() const {
    756     if (NULL == fCopy) {
    757         void* buffer = sk_malloc_throw(fBytesWritten);
    758         this->copyTo(buffer);
    759         fCopy = SkData::NewFromMalloc(buffer, fBytesWritten);
    760     }
    761     fCopy->ref();
    762     return fCopy;
    763 }
    764 
    765 void SkDynamicMemoryWStream::invalidateCopy() {
    766     if (fCopy) {
    767         fCopy->unref();
    768         fCopy = NULL;
    769     }
    770 }
    771 
    772 ///////////////////////////////////////////////////////////////////////////////
    773 
    774 void SkDebugWStream::newline()
    775 {
    776 #if defined(SK_DEBUG) || defined(SK_DEVELOPER)
    777     SkDebugf("\n");
    778 #endif
    779 }
    780 
    781 bool SkDebugWStream::write(const void* buffer, size_t size)
    782 {
    783 #if defined(SK_DEBUG) || defined(SK_DEVELOPER)
    784     char* s = new char[size+1];
    785     memcpy(s, buffer, size);
    786     s[size] = 0;
    787     SkDebugf("%s", s);
    788     delete[] s;
    789 #endif
    790     return true;
    791 }
    792