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 using namespace std;
     41 
     42 namespace WebCore {
     43 
     44 static const unsigned segmentSize = 0x1000;
     45 static const unsigned segmentPositionMask = 0x0FFF;
     46 
     47 static inline unsigned segmentIndex(unsigned position)
     48 {
     49     return position / segmentSize;
     50 }
     51 
     52 static inline unsigned offsetInSegment(unsigned position)
     53 {
     54     return position & segmentPositionMask;
     55 }
     56 
     57 static inline char* allocateSegment()
     58 {
     59     return static_cast<char*>(fastMalloc(segmentSize));
     60 }
     61 
     62 static inline void freeSegment(char* p)
     63 {
     64     fastFree(p);
     65 }
     66 
     67 #ifdef SHARED_BUFFER_STATS
     68 
     69 static Mutex& statsMutex()
     70 {
     71     DEFINE_STATIC_LOCAL(Mutex, mutex, ());
     72     return mutex;
     73 }
     74 
     75 static HashSet<SharedBuffer*>& liveBuffers()
     76 {
     77     DEFINE_STATIC_LOCAL(HashSet<SharedBuffer*>, buffers, ());
     78     return buffers;
     79 }
     80 
     81 static bool sizeComparator(SharedBuffer* a, SharedBuffer* b)
     82 {
     83     return a->size() > b->size();
     84 }
     85 
     86 static CString snippetForBuffer(SharedBuffer* sharedBuffer)
     87 {
     88     const unsigned kMaxSnippetLength = 64;
     89     char* snippet = 0;
     90     unsigned snippetLength = std::min(sharedBuffer->size(), kMaxSnippetLength);
     91     CString result = CString::newUninitialized(snippetLength, snippet);
     92 
     93     const char* segment;
     94     unsigned offset = 0;
     95     while (unsigned segmentLength = sharedBuffer->getSomeData(segment, offset)) {
     96         unsigned length = std::min(segmentLength, snippetLength - offset);
     97         memcpy(snippet + offset, segment, length);
     98         offset += segmentLength;
     99         if (offset >= snippetLength)
    100             break;
    101     }
    102 
    103     for (unsigned i = 0; i < snippetLength; ++i) {
    104         if (!isASCIIPrintable(snippet[i]))
    105             snippet[i] = '?';
    106     }
    107 
    108     return result;
    109 }
    110 
    111 static void printStats(void*)
    112 {
    113     MutexLocker locker(statsMutex());
    114     Vector<SharedBuffer*> buffers;
    115     for (HashSet<SharedBuffer*>::const_iterator iter = liveBuffers().begin(); iter != liveBuffers().end(); ++iter)
    116         buffers.append(*iter);
    117     std::sort(buffers.begin(), buffers.end(), sizeComparator);
    118 
    119     dataLogF("---- Shared Buffer Stats ----\n");
    120     for (size_t i = 0; i < buffers.size() && i < 64; ++i) {
    121         CString snippet = snippetForBuffer(buffers[i]);
    122         dataLogF("Buffer size=%8u %s\n", buffers[i]->size(), snippet.data());
    123     }
    124 }
    125 
    126 static void didCreateSharedBuffer(SharedBuffer* buffer)
    127 {
    128     MutexLocker locker(statsMutex());
    129     liveBuffers().add(buffer);
    130 
    131     callOnMainThread(printStats, 0);
    132 }
    133 
    134 static void willDestroySharedBuffer(SharedBuffer* buffer)
    135 {
    136     MutexLocker locker(statsMutex());
    137     liveBuffers().remove(buffer);
    138 }
    139 
    140 #endif
    141 
    142 SharedBuffer::SharedBuffer()
    143     : m_size(0)
    144     , m_buffer(PurgeableVector::NotPurgeable)
    145 {
    146 #ifdef SHARED_BUFFER_STATS
    147     didCreateSharedBuffer(this);
    148 #endif
    149 }
    150 
    151 SharedBuffer::SharedBuffer(size_t size)
    152     : m_size(size)
    153     , m_buffer(PurgeableVector::NotPurgeable)
    154 {
    155     m_buffer.reserveCapacity(size);
    156     m_buffer.grow(size);
    157 #ifdef SHARED_BUFFER_STATS
    158     didCreateSharedBuffer(this);
    159 #endif
    160 }
    161 
    162 SharedBuffer::SharedBuffer(const char* data, int size)
    163     : m_size(0)
    164     , m_buffer(PurgeableVector::NotPurgeable)
    165 {
    166     // FIXME: Use unsigned consistently, and check for invalid casts when calling into SharedBuffer from other code.
    167     if (size < 0)
    168         CRASH();
    169 
    170     append(data, size);
    171 
    172 #ifdef SHARED_BUFFER_STATS
    173     didCreateSharedBuffer(this);
    174 #endif
    175 }
    176 
    177 SharedBuffer::SharedBuffer(const char* data, int size, PurgeableVector::PurgeableOption purgeable)
    178     : m_size(0)
    179     , m_buffer(purgeable)
    180 {
    181     // FIXME: Use unsigned consistently, and check for invalid casts when calling into SharedBuffer from other code.
    182     if (size < 0)
    183         CRASH();
    184 
    185     append(data, size);
    186 
    187 #ifdef SHARED_BUFFER_STATS
    188     didCreateSharedBuffer(this);
    189 #endif
    190 }
    191 
    192 SharedBuffer::SharedBuffer(const unsigned char* data, int size)
    193     : m_size(0)
    194     , m_buffer(PurgeableVector::NotPurgeable)
    195 {
    196     // FIXME: Use unsigned consistently, and check for invalid casts when calling into SharedBuffer from other code.
    197     if (size < 0)
    198         CRASH();
    199 
    200     append(reinterpret_cast<const char*>(data), size);
    201 
    202 #ifdef SHARED_BUFFER_STATS
    203     didCreateSharedBuffer(this);
    204 #endif
    205 }
    206 
    207 SharedBuffer::~SharedBuffer()
    208 {
    209     clear();
    210 
    211 #ifdef SHARED_BUFFER_STATS
    212     willDestroySharedBuffer(this);
    213 #endif
    214 }
    215 
    216 PassRefPtr<SharedBuffer> SharedBuffer::adoptVector(Vector<char>& vector)
    217 {
    218     RefPtr<SharedBuffer> buffer = create();
    219     buffer->m_buffer.adopt(vector);
    220     buffer->m_size = buffer->m_buffer.size();
    221     return buffer.release();
    222 }
    223 
    224 unsigned SharedBuffer::size() const
    225 {
    226     return m_size;
    227 }
    228 
    229 const char* SharedBuffer::data() const
    230 {
    231     mergeSegmentsIntoBuffer();
    232     return m_buffer.data();
    233 }
    234 
    235 void SharedBuffer::append(SharedBuffer* data)
    236 {
    237     const char* segment;
    238     size_t position = 0;
    239     while (size_t length = data->getSomeData(segment, position)) {
    240         append(segment, length);
    241         position += length;
    242     }
    243 }
    244 
    245 void SharedBuffer::append(const char* data, unsigned length)
    246 {
    247     ASSERT(isLocked());
    248     if (!length)
    249         return;
    250 
    251     ASSERT(m_size >= m_buffer.size());
    252     unsigned positionInSegment = offsetInSegment(m_size - m_buffer.size());
    253     m_size += length;
    254 
    255     if (m_size <= segmentSize) {
    256         // No need to use segments for small resource data.
    257         m_buffer.append(data, length);
    258         return;
    259     }
    260 
    261     char* segment;
    262     if (!positionInSegment) {
    263         segment = allocateSegment();
    264         m_segments.append(segment);
    265     } else
    266         segment = m_segments.last() + positionInSegment;
    267 
    268     unsigned segmentFreeSpace = segmentSize - positionInSegment;
    269     unsigned bytesToCopy = min(length, segmentFreeSpace);
    270 
    271     for (;;) {
    272         memcpy(segment, data, bytesToCopy);
    273         if (static_cast<unsigned>(length) == bytesToCopy)
    274             break;
    275 
    276         length -= bytesToCopy;
    277         data += bytesToCopy;
    278         segment = allocateSegment();
    279         m_segments.append(segment);
    280         bytesToCopy = min(length, segmentSize);
    281     }
    282 }
    283 
    284 void SharedBuffer::append(const Vector<char>& data)
    285 {
    286     append(data.data(), data.size());
    287 }
    288 
    289 void SharedBuffer::clear()
    290 {
    291     for (unsigned i = 0; i < m_segments.size(); ++i)
    292         freeSegment(m_segments[i]);
    293 
    294     m_segments.clear();
    295     m_size = 0;
    296     m_buffer.clear();
    297 }
    298 
    299 PassRefPtr<SharedBuffer> SharedBuffer::copy() const
    300 {
    301     RefPtr<SharedBuffer> clone(adoptRef(new SharedBuffer));
    302     clone->m_size = m_size;
    303     clone->m_buffer.reserveCapacity(m_size);
    304     clone->m_buffer.append(m_buffer.data(), m_buffer.size());
    305     if (!m_segments.isEmpty()) {
    306         const char* segment = 0;
    307         unsigned position = m_buffer.size();
    308         while (unsigned segmentSize = getSomeData(segment, position)) {
    309             clone->m_buffer.append(segment, segmentSize);
    310             position += segmentSize;
    311         }
    312         ASSERT(position == clone->size());
    313     }
    314     return clone.release();
    315 }
    316 
    317 void SharedBuffer::mergeSegmentsIntoBuffer() const
    318 {
    319     unsigned bufferSize = m_buffer.size();
    320     if (m_size > bufferSize) {
    321         m_buffer.reserveCapacity(m_size);
    322         unsigned bytesLeft = m_size - bufferSize;
    323         for (unsigned i = 0; i < m_segments.size(); ++i) {
    324             unsigned bytesToCopy = min(bytesLeft, segmentSize);
    325             m_buffer.append(m_segments[i], bytesToCopy);
    326             bytesLeft -= bytesToCopy;
    327             freeSegment(m_segments[i]);
    328         }
    329         m_segments.clear();
    330     }
    331 }
    332 
    333 unsigned SharedBuffer::getSomeData(const char*& someData, unsigned position) const
    334 {
    335     ASSERT(isLocked());
    336     unsigned totalSize = size();
    337     if (position >= totalSize) {
    338         someData = 0;
    339         return 0;
    340     }
    341 
    342     ASSERT_WITH_SECURITY_IMPLICATION(position < m_size);
    343     unsigned consecutiveSize = m_buffer.size();
    344     if (position < consecutiveSize) {
    345         someData = m_buffer.data() + position;
    346         return consecutiveSize - position;
    347     }
    348 
    349     position -= consecutiveSize;
    350     unsigned segments = m_segments.size();
    351     unsigned maxSegmentedSize = segments * segmentSize;
    352     unsigned segment = segmentIndex(position);
    353     if (segment < segments) {
    354         unsigned bytesLeft = totalSize - consecutiveSize;
    355         unsigned segmentedSize = min(maxSegmentedSize, bytesLeft);
    356 
    357         unsigned positionInSegment = offsetInSegment(position);
    358         someData = m_segments[segment] + positionInSegment;
    359         return segment == segments - 1 ? segmentedSize - position : segmentSize - positionInSegment;
    360     }
    361     ASSERT_NOT_REACHED();
    362     return 0;
    363 }
    364 
    365 PassRefPtr<ArrayBuffer> SharedBuffer::getAsArrayBuffer() const
    366 {
    367     RefPtr<ArrayBuffer> arrayBuffer = ArrayBuffer::createUninitialized(static_cast<unsigned>(size()), 1);
    368 
    369     if (!arrayBuffer)
    370         return nullptr;
    371 
    372     const char* segment = 0;
    373     unsigned position = 0;
    374     while (unsigned segmentSize = getSomeData(segment, position)) {
    375         memcpy(static_cast<char*>(arrayBuffer->data()) + position, segment, segmentSize);
    376         position += segmentSize;
    377     }
    378 
    379     if (position != arrayBuffer->byteLength()) {
    380         ASSERT_NOT_REACHED();
    381         // Don't return the incomplete ArrayBuffer.
    382         return nullptr;
    383     }
    384 
    385     return arrayBuffer;
    386 }
    387 
    388 PassRefPtr<SkData> SharedBuffer::getAsSkData() const
    389 {
    390     unsigned bufferLength = size();
    391     char* buffer = static_cast<char*>(sk_malloc_throw(bufferLength));
    392     const char* segment = 0;
    393     unsigned position = 0;
    394     while (unsigned segmentSize = getSomeData(segment, position)) {
    395         memcpy(buffer + position, segment, segmentSize);
    396         position += segmentSize;
    397     }
    398 
    399     if (position != bufferLength) {
    400         ASSERT_NOT_REACHED();
    401         // Don't return the incomplete SkData.
    402         return nullptr;
    403     }
    404     return adoptRef(SkData::NewFromMalloc(buffer, bufferLength));
    405 }
    406 
    407 bool SharedBuffer::lock()
    408 {
    409     return m_buffer.lock();
    410 }
    411 
    412 void SharedBuffer::unlock()
    413 {
    414     mergeSegmentsIntoBuffer();
    415     m_buffer.unlock();
    416 }
    417 
    418 bool SharedBuffer::isLocked() const
    419 {
    420     return m_buffer.isLocked();
    421 }
    422 
    423 } // namespace WebCore
    424