/* * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "platform/SharedBuffer.h" #include "platform/PurgeableBuffer.h" #include "wtf/PassOwnPtr.h" #include "wtf/unicode/Unicode.h" #include "wtf/unicode/UTF8.h" #undef SHARED_BUFFER_STATS #ifdef SHARED_BUFFER_STATS #include "wtf/DataLog.h" #include "wtf/MainThread.h" #endif using namespace std; namespace WebCore { static const unsigned segmentSize = 0x1000; static const unsigned segmentPositionMask = 0x0FFF; static inline unsigned segmentIndex(unsigned position) { return position / segmentSize; } static inline unsigned offsetInSegment(unsigned position) { return position & segmentPositionMask; } static inline char* allocateSegment() { return static_cast(fastMalloc(segmentSize)); } static inline void freeSegment(char* p) { fastFree(p); } #ifdef SHARED_BUFFER_STATS static Mutex& statsMutex() { DEFINE_STATIC_LOCAL(Mutex, mutex, ()); return mutex; } static HashSet& liveBuffers() { DEFINE_STATIC_LOCAL(HashSet, buffers, ()); return buffers; } static bool sizeComparator(SharedBuffer* a, SharedBuffer* b) { return a->size() > b->size(); } static CString snippetForBuffer(SharedBuffer* sharedBuffer) { const unsigned kMaxSnippetLength = 64; char* snippet = 0; unsigned snippetLength = std::min(sharedBuffer->size(), kMaxSnippetLength); CString result = CString::newUninitialized(snippetLength, snippet); const char* segment; unsigned offset = 0; while (unsigned segmentLength = sharedBuffer->getSomeData(segment, offset)) { unsigned length = std::min(segmentLength, snippetLength - offset); memcpy(snippet + offset, segment, length); offset += segmentLength; if (offset >= snippetLength) break; } for (unsigned i = 0; i < snippetLength; ++i) { if (!isASCIIPrintable(snippet[i])) snippet[i] = '?'; } return result; } static void printStats(void*) { MutexLocker locker(statsMutex()); Vector buffers; for (HashSet::const_iterator iter = liveBuffers().begin(); iter != liveBuffers().end(); ++iter) buffers.append(*iter); std::sort(buffers.begin(), buffers.end(), sizeComparator); dataLogF("---- Shared Buffer Stats ----\n"); for (size_t i = 0; i < buffers.size() && i < 64; ++i) { CString snippet = snippetForBuffer(buffers[i]); dataLogF("Buffer size=%8u %s\n", buffers[i]->size(), snippet.data()); } } static void didCreateSharedBuffer(SharedBuffer* buffer) { MutexLocker locker(statsMutex()); liveBuffers().add(buffer); callOnMainThread(printStats, 0); } static void willDestroySharedBuffer(SharedBuffer* buffer) { MutexLocker locker(statsMutex()); liveBuffers().remove(buffer); } #endif SharedBuffer::SharedBuffer() : m_size(0) { #ifdef SHARED_BUFFER_STATS didCreateSharedBuffer(this); #endif } SharedBuffer::SharedBuffer(size_t size) : m_size(size) , m_buffer(size) { #ifdef SHARED_BUFFER_STATS didCreateSharedBuffer(this); #endif } SharedBuffer::SharedBuffer(const char* data, int size) : m_size(0) { // FIXME: Use unsigned consistently, and check for invalid casts when calling into SharedBuffer from other code. if (size < 0) CRASH(); append(data, size); #ifdef SHARED_BUFFER_STATS didCreateSharedBuffer(this); #endif } SharedBuffer::SharedBuffer(const unsigned char* data, int size) : m_size(0) { // FIXME: Use unsigned consistently, and check for invalid casts when calling into SharedBuffer from other code. if (size < 0) CRASH(); append(reinterpret_cast(data), size); #ifdef SHARED_BUFFER_STATS didCreateSharedBuffer(this); #endif } SharedBuffer::~SharedBuffer() { clear(); #ifdef SHARED_BUFFER_STATS willDestroySharedBuffer(this); #endif } PassRefPtr SharedBuffer::adoptVector(Vector& vector) { RefPtr buffer = create(); buffer->m_buffer.swap(vector); buffer->m_size = buffer->m_buffer.size(); return buffer.release(); } PassRefPtr SharedBuffer::adoptPurgeableBuffer(PassOwnPtr purgeableBuffer) { ASSERT(!purgeableBuffer->isPurgeable()); RefPtr buffer = create(); buffer->m_purgeableBuffer = purgeableBuffer; return buffer.release(); } unsigned SharedBuffer::size() const { if (m_purgeableBuffer) return m_purgeableBuffer->size(); return m_size; } void SharedBuffer::createPurgeableBuffer() const { if (m_purgeableBuffer) return; m_purgeableBuffer = PurgeableBuffer::create(buffer().data(), m_size); } const char* SharedBuffer::data() const { if (m_purgeableBuffer) return m_purgeableBuffer->data(); return this->buffer().data(); } void SharedBuffer::moveTo(Vector& result) { ASSERT(result.isEmpty()); if (m_purgeableBuffer) { result.reserveCapacity(m_purgeableBuffer->size()); result.append(m_purgeableBuffer->data(), m_purgeableBuffer->size()); clear(); return; } unsigned bufferSize = m_buffer.size(); if (m_size == bufferSize) { m_buffer.swap(result); clear(); return; } result.reserveCapacity(m_size); const char* segment = 0; unsigned position = 0; while (unsigned segmentSize = getSomeData(segment, position)) { result.append(segment, segmentSize); position += segmentSize; } ASSERT(result.size() == m_size); clear(); return; } void SharedBuffer::append(SharedBuffer* data) { const char* segment; size_t position = 0; while (size_t length = data->getSomeData(segment, position)) { append(segment, length); position += length; } } void SharedBuffer::append(const char* data, unsigned length) { ASSERT(!m_purgeableBuffer); if (!length) return; unsigned positionInSegment = offsetInSegment(m_size - m_buffer.size()); m_size += length; if (m_size <= segmentSize) { // No need to use segments for small resource data if (m_buffer.isEmpty()) m_buffer.reserveInitialCapacity(length); m_buffer.append(data, length); return; } char* segment; if (!positionInSegment) { segment = allocateSegment(); m_segments.append(segment); } else segment = m_segments.last() + positionInSegment; unsigned segmentFreeSpace = segmentSize - positionInSegment; unsigned bytesToCopy = min(length, segmentFreeSpace); for (;;) { memcpy(segment, data, bytesToCopy); if (static_cast(length) == bytesToCopy) break; length -= bytesToCopy; data += bytesToCopy; segment = allocateSegment(); m_segments.append(segment); bytesToCopy = min(length, segmentSize); } } void SharedBuffer::append(const Vector& data) { append(data.data(), data.size()); } void SharedBuffer::clear() { for (unsigned i = 0; i < m_segments.size(); ++i) freeSegment(m_segments[i]); m_segments.clear(); m_size = 0; m_buffer.clear(); m_purgeableBuffer.clear(); } PassRefPtr SharedBuffer::copy() const { RefPtr clone(adoptRef(new SharedBuffer)); if (m_purgeableBuffer) { clone->append(data(), size()); return clone.release(); } clone->m_size = m_size; clone->m_buffer.reserveCapacity(m_size); clone->m_buffer.append(m_buffer.data(), m_buffer.size()); if (!m_segments.isEmpty()) { const char* segment = 0; unsigned position = m_buffer.size(); while (unsigned segmentSize = getSomeData(segment, position)) { clone->m_buffer.append(segment, segmentSize); position += segmentSize; } ASSERT(position == clone->size()); } return clone.release(); } PassOwnPtr SharedBuffer::releasePurgeableBuffer() { ASSERT(hasOneRef()); return m_purgeableBuffer.release(); } const Vector& SharedBuffer::buffer() const { unsigned bufferSize = m_buffer.size(); if (m_size > bufferSize) { m_buffer.resize(m_size); char* destination = m_buffer.data() + bufferSize; unsigned bytesLeft = m_size - bufferSize; for (unsigned i = 0; i < m_segments.size(); ++i) { unsigned bytesToCopy = min(bytesLeft, segmentSize); memcpy(destination, m_segments[i], bytesToCopy); destination += bytesToCopy; bytesLeft -= bytesToCopy; freeSegment(m_segments[i]); } m_segments.clear(); } return m_buffer; } unsigned SharedBuffer::getSomeData(const char*& someData, unsigned position) const { unsigned totalSize = size(); if (position >= totalSize) { someData = 0; return 0; } if (m_purgeableBuffer) { ASSERT_WITH_SECURITY_IMPLICATION(position < size()); someData = data() + position; return totalSize - position; } ASSERT_WITH_SECURITY_IMPLICATION(position < m_size); unsigned consecutiveSize = m_buffer.size(); if (position < consecutiveSize) { someData = m_buffer.data() + position; return consecutiveSize - position; } position -= consecutiveSize; unsigned segments = m_segments.size(); unsigned maxSegmentedSize = segments * segmentSize; unsigned segment = segmentIndex(position); if (segment < segments) { unsigned bytesLeft = totalSize - consecutiveSize; unsigned segmentedSize = min(maxSegmentedSize, bytesLeft); unsigned positionInSegment = offsetInSegment(position); someData = m_segments[segment] + positionInSegment; return segment == segments - 1 ? segmentedSize - position : segmentSize - positionInSegment; } ASSERT_NOT_REACHED(); return 0; } PassRefPtr SharedBuffer::getAsArrayBuffer() const { RefPtr arrayBuffer = ArrayBuffer::createUninitialized(static_cast(size()), 1); const char* segment = 0; unsigned position = 0; while (unsigned segmentSize = getSomeData(segment, position)) { memcpy(static_cast(arrayBuffer->data()) + position, segment, segmentSize); position += segmentSize; } if (position != arrayBuffer->byteLength()) { ASSERT_NOT_REACHED(); // Don't return the incomplete ArrayBuffer. return 0; } return arrayBuffer; } PassRefPtr SharedBuffer::getAsSkData() const { unsigned bufferLength = size(); char* buffer = static_cast(sk_malloc_throw(bufferLength)); const char* segment = 0; unsigned position = 0; while (unsigned segmentSize = getSomeData(segment, position)) { memcpy(buffer + position, segment, segmentSize); position += segmentSize; } if (position != bufferLength) { ASSERT_NOT_REACHED(); // Don't return the incomplete SkData. return 0; } return adoptRef(SkData::NewFromMalloc(buffer, bufferLength)); } } // namespace WebCore