Home | History | Annotate | Download | only in platform
      1 /*
      2  * Copyright (C) 2006, 2008 Apple Inc. All rights reserved.
      3  * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  * 1. Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  * 2. Redistributions in binary form must reproduce the above copyright
     11  *    notice, this list of conditions and the following disclaimer in the
     12  *    documentation and/or other materials provided with the distribution.
     13  *
     14  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
     15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
     18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     25  */
     26 
     27 #include "config.h"
     28 #include "platform/SharedBuffer.h"
     29 
     30 #include "wtf/unicode/Unicode.h"
     31 #include "wtf/unicode/UTF8.h"
     32 
     33 #undef SHARED_BUFFER_STATS
     34 
     35 #ifdef SHARED_BUFFER_STATS
     36 #include "wtf/DataLog.h"
     37 #include "wtf/MainThread.h"
     38 #endif
     39 
     40 namespace blink {
     41 
     42 static const unsigned segmentSize = 0x1000;
     43 static const unsigned segmentPositionMask = 0x0FFF;
     44 
     45 static inline unsigned segmentIndex(unsigned position)
     46 {
     47     return position / segmentSize;
     48 }
     49 
     50 static inline unsigned offsetInSegment(unsigned position)
     51 {
     52     return position & segmentPositionMask;
     53 }
     54 
     55 static inline char* allocateSegment()
     56 {
     57     return static_cast<char*>(fastMalloc(segmentSize));
     58 }
     59 
     60 static inline void freeSegment(char* p)
     61 {
     62     fastFree(p);
     63 }
     64 
     65 #ifdef SHARED_BUFFER_STATS
     66 
     67 static Mutex& statsMutex()
     68 {
     69     DEFINE_STATIC_LOCAL(Mutex, mutex, ());
     70     return mutex;
     71 }
     72 
     73 static HashSet<SharedBuffer*>& liveBuffers()
     74 {
     75     DEFINE_STATIC_LOCAL(HashSet<SharedBuffer*>, buffers, ());
     76     return buffers;
     77 }
     78 
     79 static bool sizeComparator(SharedBuffer* a, SharedBuffer* b)
     80 {
     81     return a->size() > b->size();
     82 }
     83 
     84 static CString snippetForBuffer(SharedBuffer* sharedBuffer)
     85 {
     86     const unsigned kMaxSnippetLength = 64;
     87     char* snippet = 0;
     88     unsigned snippetLength = std::min(sharedBuffer->size(), kMaxSnippetLength);
     89     CString result = CString::newUninitialized(snippetLength, snippet);
     90 
     91     const char* segment;
     92     unsigned offset = 0;
     93     while (unsigned segmentLength = sharedBuffer->getSomeData(segment, offset)) {
     94         unsigned length = std::min(segmentLength, snippetLength - offset);
     95         memcpy(snippet + offset, segment, length);
     96         offset += segmentLength;
     97         if (offset >= snippetLength)
     98             break;
     99     }
    100 
    101     for (unsigned i = 0; i < snippetLength; ++i) {
    102         if (!isASCIIPrintable(snippet[i]))
    103             snippet[i] = '?';
    104     }
    105 
    106     return result;
    107 }
    108 
    109 static void printStats(void*)
    110 {
    111     MutexLocker locker(statsMutex());
    112     Vector<SharedBuffer*> buffers;
    113     for (HashSet<SharedBuffer*>::const_iterator iter = liveBuffers().begin(); iter != liveBuffers().end(); ++iter)
    114         buffers.append(*iter);
    115     std::sort(buffers.begin(), buffers.end(), sizeComparator);
    116 
    117     dataLogF("---- Shared Buffer Stats ----\n");
    118     for (size_t i = 0; i < buffers.size() && i < 64; ++i) {
    119         CString snippet = snippetForBuffer(buffers[i]);
    120         dataLogF("Buffer size=%8u %s\n", buffers[i]->size(), snippet.data());
    121     }
    122 }
    123 
    124 static void didCreateSharedBuffer(SharedBuffer* buffer)
    125 {
    126     MutexLocker locker(statsMutex());
    127     liveBuffers().add(buffer);
    128 
    129     callOnMainThread(printStats, 0);
    130 }
    131 
    132 static void willDestroySharedBuffer(SharedBuffer* buffer)
    133 {
    134     MutexLocker locker(statsMutex());
    135     liveBuffers().remove(buffer);
    136 }
    137 
    138 #endif
    139 
    140 SharedBuffer::SharedBuffer()
    141     : m_size(0)
    142     , m_buffer(PurgeableVector::NotPurgeable)
    143 {
    144 #ifdef SHARED_BUFFER_STATS
    145     didCreateSharedBuffer(this);
    146 #endif
    147 }
    148 
    149 SharedBuffer::SharedBuffer(size_t size)
    150     : m_size(size)
    151     , m_buffer(PurgeableVector::NotPurgeable)
    152 {
    153     m_buffer.reserveCapacity(size);
    154     m_buffer.grow(size);
    155 #ifdef SHARED_BUFFER_STATS
    156     didCreateSharedBuffer(this);
    157 #endif
    158 }
    159 
    160 SharedBuffer::SharedBuffer(const char* data, int size)
    161     : m_size(0)
    162     , m_buffer(PurgeableVector::NotPurgeable)
    163 {
    164     // FIXME: Use unsigned consistently, and check for invalid casts when calling into SharedBuffer from other code.
    165     if (size < 0)
    166         CRASH();
    167 
    168     append(data, size);
    169 
    170 #ifdef SHARED_BUFFER_STATS
    171     didCreateSharedBuffer(this);
    172 #endif
    173 }
    174 
    175 SharedBuffer::SharedBuffer(const char* data, int size, PurgeableVector::PurgeableOption purgeable)
    176     : m_size(0)
    177     , m_buffer(purgeable)
    178 {
    179     // FIXME: Use unsigned consistently, and check for invalid casts when calling into SharedBuffer from other code.
    180     if (size < 0)
    181         CRASH();
    182 
    183     append(data, size);
    184 
    185 #ifdef SHARED_BUFFER_STATS
    186     didCreateSharedBuffer(this);
    187 #endif
    188 }
    189 
    190 SharedBuffer::SharedBuffer(const unsigned char* data, int size)
    191     : m_size(0)
    192     , m_buffer(PurgeableVector::NotPurgeable)
    193 {
    194     // FIXME: Use unsigned consistently, and check for invalid casts when calling into SharedBuffer from other code.
    195     if (size < 0)
    196         CRASH();
    197 
    198     append(reinterpret_cast<const char*>(data), size);
    199 
    200 #ifdef SHARED_BUFFER_STATS
    201     didCreateSharedBuffer(this);
    202 #endif
    203 }
    204 
    205 SharedBuffer::~SharedBuffer()
    206 {
    207     clear();
    208 
    209 #ifdef SHARED_BUFFER_STATS
    210     willDestroySharedBuffer(this);
    211 #endif
    212 }
    213 
    214 PassRefPtr<SharedBuffer> SharedBuffer::adoptVector(Vector<char>& vector)
    215 {
    216     RefPtr<SharedBuffer> buffer = create();
    217     buffer->m_buffer.adopt(vector);
    218     buffer->m_size = buffer->m_buffer.size();
    219     return buffer.release();
    220 }
    221 
    222 unsigned SharedBuffer::size() const
    223 {
    224     return m_size;
    225 }
    226 
    227 const char* SharedBuffer::data() const
    228 {
    229     mergeSegmentsIntoBuffer();
    230     return m_buffer.data();
    231 }
    232 
    233 void SharedBuffer::append(PassRefPtr<SharedBuffer> data)
    234 {
    235     const char* segment;
    236     size_t position = 0;
    237     while (size_t length = data->getSomeData(segment, position)) {
    238         append(segment, length);
    239         position += length;
    240     }
    241 }
    242 
    243 void SharedBuffer::append(const char* data, unsigned length)
    244 {
    245     ASSERT(isLocked());
    246     if (!length)
    247         return;
    248 
    249     ASSERT(m_size >= m_buffer.size());
    250     unsigned positionInSegment = offsetInSegment(m_size - m_buffer.size());
    251     m_size += length;
    252 
    253     if (m_size <= segmentSize) {
    254         // No need to use segments for small resource data.
    255         m_buffer.append(data, length);
    256         return;
    257     }
    258 
    259     char* segment;
    260     if (!positionInSegment) {
    261         segment = allocateSegment();
    262         m_segments.append(segment);
    263     } else
    264         segment = m_segments.last() + positionInSegment;
    265 
    266     unsigned segmentFreeSpace = segmentSize - positionInSegment;
    267     unsigned bytesToCopy = std::min(length, segmentFreeSpace);
    268 
    269     for (;;) {
    270         memcpy(segment, data, bytesToCopy);
    271         if (static_cast<unsigned>(length) == bytesToCopy)
    272             break;
    273 
    274         length -= bytesToCopy;
    275         data += bytesToCopy;
    276         segment = allocateSegment();
    277         m_segments.append(segment);
    278         bytesToCopy = std::min(length, segmentSize);
    279     }
    280 }
    281 
    282 void SharedBuffer::append(const Vector<char>& data)
    283 {
    284     append(data.data(), data.size());
    285 }
    286 
    287 void SharedBuffer::clear()
    288 {
    289     for (unsigned i = 0; i < m_segments.size(); ++i)
    290         freeSegment(m_segments[i]);
    291 
    292     m_segments.clear();
    293     m_size = 0;
    294     m_buffer.clear();
    295 }
    296 
    297 PassRefPtr<SharedBuffer> SharedBuffer::copy() const
    298 {
    299     RefPtr<SharedBuffer> clone(adoptRef(new SharedBuffer));
    300     clone->m_size = m_size;
    301     clone->m_buffer.reserveCapacity(m_size);
    302     clone->m_buffer.append(m_buffer.data(), m_buffer.size());
    303     if (!m_segments.isEmpty()) {
    304         const char* segment = 0;
    305         unsigned position = m_buffer.size();
    306         while (unsigned segmentSize = getSomeData(segment, position)) {
    307             clone->m_buffer.append(segment, segmentSize);
    308             position += segmentSize;
    309         }
    310         ASSERT(position == clone->size());
    311     }
    312     return clone.release();
    313 }
    314 
    315 void SharedBuffer::mergeSegmentsIntoBuffer() const
    316 {
    317     unsigned bufferSize = m_buffer.size();
    318     if (m_size > bufferSize) {
    319         m_buffer.reserveCapacity(m_size);
    320         unsigned bytesLeft = m_size - bufferSize;
    321         for (unsigned i = 0; i < m_segments.size(); ++i) {
    322             unsigned bytesToCopy = std::min(bytesLeft, segmentSize);
    323             m_buffer.append(m_segments[i], bytesToCopy);
    324             bytesLeft -= bytesToCopy;
    325             freeSegment(m_segments[i]);
    326         }
    327         m_segments.clear();
    328     }
    329 }
    330 
    331 unsigned SharedBuffer::getSomeData(const char*& someData, unsigned position) const
    332 {
    333     ASSERT(isLocked());
    334     unsigned totalSize = size();
    335     if (position >= totalSize) {
    336         someData = 0;
    337         return 0;
    338     }
    339 
    340     ASSERT_WITH_SECURITY_IMPLICATION(position < m_size);
    341     unsigned consecutiveSize = m_buffer.size();
    342     if (position < consecutiveSize) {
    343         someData = m_buffer.data() + position;
    344         return consecutiveSize - position;
    345     }
    346 
    347     position -= consecutiveSize;
    348     unsigned segments = m_segments.size();
    349     unsigned maxSegmentedSize = segments * segmentSize;
    350     unsigned segment = segmentIndex(position);
    351     if (segment < segments) {
    352         unsigned bytesLeft = totalSize - consecutiveSize;
    353         unsigned segmentedSize = std::min(maxSegmentedSize, bytesLeft);
    354 
    355         unsigned positionInSegment = offsetInSegment(position);
    356         someData = m_segments[segment] + positionInSegment;
    357         return segment == segments - 1 ? segmentedSize - position : segmentSize - positionInSegment;
    358     }
    359     ASSERT_NOT_REACHED();
    360     return 0;
    361 }
    362 
    363 PassRefPtr<ArrayBuffer> SharedBuffer::getAsArrayBuffer() const
    364 {
    365     RefPtr<ArrayBuffer> arrayBuffer = ArrayBuffer::createUninitialized(static_cast<unsigned>(size()), 1);
    366 
    367     if (!arrayBuffer)
    368         return nullptr;
    369 
    370     const char* segment = 0;
    371     unsigned position = 0;
    372     while (unsigned segmentSize = getSomeData(segment, position)) {
    373         memcpy(static_cast<char*>(arrayBuffer->data()) + position, segment, segmentSize);
    374         position += segmentSize;
    375     }
    376 
    377     if (position != arrayBuffer->byteLength()) {
    378         ASSERT_NOT_REACHED();
    379         // Don't return the incomplete ArrayBuffer.
    380         return nullptr;
    381     }
    382 
    383     return arrayBuffer;
    384 }
    385 
    386 PassRefPtr<SkData> SharedBuffer::getAsSkData() const
    387 {
    388     unsigned bufferLength = size();
    389     SkData* data = SkData::NewUninitialized(bufferLength);
    390     char* buffer = static_cast<char*>(data->writable_data());
    391     const char* segment = 0;
    392     unsigned position = 0;
    393     while (unsigned segmentSize = getSomeData(segment, position)) {
    394         memcpy(buffer + position, segment, segmentSize);
    395         position += segmentSize;
    396     }
    397 
    398     if (position != bufferLength) {
    399         ASSERT_NOT_REACHED();
    400         // Don't return the incomplete SkData.
    401         return nullptr;
    402     }
    403     return adoptRef(data);
    404 }
    405 
    406 bool SharedBuffer::lock()
    407 {
    408     return m_buffer.lock();
    409 }
    410 
    411 void SharedBuffer::unlock()
    412 {
    413     mergeSegmentsIntoBuffer();
    414     m_buffer.unlock();
    415 }
    416 
    417 bool SharedBuffer::isLocked() const
    418 {
    419     return m_buffer.isLocked();
    420 }
    421 
    422 } // namespace blink
    423