/* * 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 "flutter/sky/engine/platform/SharedBuffer.h" #include "flutter/common/threads.h" #include "flutter/sky/engine/public/platform/Platform.h" #include "flutter/sky/engine/wtf/unicode/UTF8.h" #include "flutter/sky/engine/wtf/unicode/Unicode.h" #undef SHARED_BUFFER_STATS #ifdef SHARED_BUFFER_STATS #include "flutter/sky/engine/wtf/DataLog.h" #endif namespace blink { 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() { 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); Threads::UI()->PostTask(printStats); } static void willDestroySharedBuffer(SharedBuffer* buffer) { MutexLocker locker(statsMutex()); liveBuffers().remove(buffer); } #endif SharedBuffer::SharedBuffer() : m_size(0), m_buffer(PurgeableVector::NotPurgeable) { #ifdef SHARED_BUFFER_STATS didCreateSharedBuffer(this); #endif } SharedBuffer::SharedBuffer(size_t size) : m_size(size), m_buffer(PurgeableVector::NotPurgeable) { m_buffer.reserveCapacity(size); m_buffer.grow(size); #ifdef SHARED_BUFFER_STATS didCreateSharedBuffer(this); #endif } SharedBuffer::SharedBuffer(const char* data, int size) : m_size(0), m_buffer(PurgeableVector::NotPurgeable) { // 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 char* data, int size, PurgeableVector::PurgeableOption purgeable) : m_size(0), m_buffer(purgeable) { // 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), m_buffer(PurgeableVector::NotPurgeable) { // 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.adopt(vector); buffer->m_size = buffer->m_buffer.size(); return buffer.release(); } unsigned SharedBuffer::size() const { return m_size; } const char* SharedBuffer::data() const { mergeSegmentsIntoBuffer(); return m_buffer.data(); } void SharedBuffer::append(PassRefPtr 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(isLocked()); if (!length) return; ASSERT(m_size >= m_buffer.size()); unsigned positionInSegment = offsetInSegment(m_size - m_buffer.size()); m_size += length; if (m_size <= segmentSize) { // No need to use segments for small resource data. 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 = std::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 = std::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(); } PassRefPtr SharedBuffer::copy() const { RefPtr clone(adoptRef(new SharedBuffer)); 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(); } void SharedBuffer::mergeSegmentsIntoBuffer() const { unsigned bufferSize = m_buffer.size(); if (m_size > bufferSize) { m_buffer.reserveCapacity(m_size); unsigned bytesLeft = m_size - bufferSize; for (unsigned i = 0; i < m_segments.size(); ++i) { unsigned bytesToCopy = std::min(bytesLeft, segmentSize); m_buffer.append(m_segments[i], bytesToCopy); bytesLeft -= bytesToCopy; freeSegment(m_segments[i]); } m_segments.clear(); } } unsigned SharedBuffer::getSomeData(const char*& someData, unsigned position) const { ASSERT(isLocked()); unsigned totalSize = size(); if (position >= totalSize) { someData = 0; return 0; } 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 = std::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; } sk_sp 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 nullptr; } return SkData::MakeFromMalloc(buffer, bufferLength); } bool SharedBuffer::lock() { return m_buffer.lock(); } void SharedBuffer::unlock() { mergeSegmentsIntoBuffer(); m_buffer.unlock(); } bool SharedBuffer::isLocked() const { return m_buffer.isLocked(); } } // namespace blink