mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
This caused us to lose our gn check certification. :( Turns out gn check was just ignoring all the header paths it didn't understand and so gn check passing for sky wasn't meaning much. I tried to straighten out some of the mess in this CL, but its going to take several more rounds of massaging before gn check passes again. On the bright side (almost) all of our headers are absolute now. Turns out my script (attached to the bug) didn't notice ../ includes but I'll fix that in the next patch. R=abarth@chromium.org BUG=435361 Review URL: https://codereview.chromium.org/746023002
423 lines
11 KiB
C++
423 lines
11 KiB
C++
/*
|
|
* 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 "sky/engine/config.h"
|
|
#include "sky/engine/platform/SharedBuffer.h"
|
|
|
|
#include "base/bind.h"
|
|
#include "sky/engine/public/platform/Platform.h"
|
|
#include "sky/engine/wtf/unicode/UTF8.h"
|
|
#include "sky/engine/wtf/unicode/Unicode.h"
|
|
|
|
#undef SHARED_BUFFER_STATS
|
|
|
|
#ifdef SHARED_BUFFER_STATS
|
|
#include "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<char*>(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<SharedBuffer*>& liveBuffers()
|
|
{
|
|
DEFINE_STATIC_LOCAL(HashSet<SharedBuffer*>, 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<SharedBuffer*> buffers;
|
|
for (HashSet<SharedBuffer*>::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);
|
|
|
|
Platform::current()->mainThreadTaskRunner()->PostTask(FROM_HERE, base::Bind(&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<const char*>(data), size);
|
|
|
|
#ifdef SHARED_BUFFER_STATS
|
|
didCreateSharedBuffer(this);
|
|
#endif
|
|
}
|
|
|
|
SharedBuffer::~SharedBuffer()
|
|
{
|
|
clear();
|
|
|
|
#ifdef SHARED_BUFFER_STATS
|
|
willDestroySharedBuffer(this);
|
|
#endif
|
|
}
|
|
|
|
PassRefPtr<SharedBuffer> SharedBuffer::adoptVector(Vector<char>& vector)
|
|
{
|
|
RefPtr<SharedBuffer> 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<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(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<unsigned>(length) == bytesToCopy)
|
|
break;
|
|
|
|
length -= bytesToCopy;
|
|
data += bytesToCopy;
|
|
segment = allocateSegment();
|
|
m_segments.append(segment);
|
|
bytesToCopy = std::min(length, segmentSize);
|
|
}
|
|
}
|
|
|
|
void SharedBuffer::append(const Vector<char>& 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> SharedBuffer::copy() const
|
|
{
|
|
RefPtr<SharedBuffer> 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;
|
|
}
|
|
|
|
PassRefPtr<ArrayBuffer> SharedBuffer::getAsArrayBuffer() const
|
|
{
|
|
RefPtr<ArrayBuffer> arrayBuffer = ArrayBuffer::createUninitialized(static_cast<unsigned>(size()), 1);
|
|
|
|
if (!arrayBuffer)
|
|
return nullptr;
|
|
|
|
const char* segment = 0;
|
|
unsigned position = 0;
|
|
while (unsigned segmentSize = getSomeData(segment, position)) {
|
|
memcpy(static_cast<char*>(arrayBuffer->data()) + position, segment, segmentSize);
|
|
position += segmentSize;
|
|
}
|
|
|
|
if (position != arrayBuffer->byteLength()) {
|
|
ASSERT_NOT_REACHED();
|
|
// Don't return the incomplete ArrayBuffer.
|
|
return nullptr;
|
|
}
|
|
|
|
return arrayBuffer;
|
|
}
|
|
|
|
PassRefPtr<SkData> SharedBuffer::getAsSkData() const
|
|
{
|
|
unsigned bufferLength = size();
|
|
char* buffer = static_cast<char*>(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 adoptRef(SkData::NewFromMalloc(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
|