blob: 1804a886daf10847fd4feb6b469b61b9974b48f6 [file] [log] [blame]
/*
* 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 "platform/SharedBuffer.h"
#include "platform/instrumentation/tracing/web_process_memory_dump.h"
#include "platform/wtf/text/UTF8.h"
#include "platform/wtf/text/Unicode.h"
#include "third_party/skia/include/core/SkData.h"
namespace blink {
static inline size_t SegmentIndex(size_t position) {
return position / SharedBuffer::kSegmentSize;
}
static inline size_t OffsetInSegment(size_t position) {
return position % SharedBuffer::kSegmentSize;
}
static inline char* AllocateSegment() {
return static_cast<char*>(WTF::Partitions::FastMalloc(
SharedBuffer::kSegmentSize, "blink::SharedBuffer"));
}
static inline void FreeSegment(char* p) {
WTF::Partitions::FastFree(p);
}
SharedBuffer::SharedBuffer() : size_(0) {}
SharedBuffer::SharedBuffer(size_t size) : size_(size), buffer_(size) {}
SharedBuffer::SharedBuffer(const char* data, size_t size) : size_(0) {
AppendInternal(data, size);
}
SharedBuffer::SharedBuffer(const unsigned char* data, size_t size) : size_(0) {
AppendInternal(reinterpret_cast<const char*>(data), size);
}
SharedBuffer::~SharedBuffer() {
Clear();
}
scoped_refptr<SharedBuffer> SharedBuffer::AdoptVector(Vector<char>& vector) {
scoped_refptr<SharedBuffer> buffer = Create();
buffer->buffer_.swap(vector);
buffer->size_ = buffer->buffer_.size();
return buffer;
}
size_t SharedBuffer::size() const {
return size_;
}
const char* SharedBuffer::Data() const {
MergeSegmentsIntoBuffer();
return buffer_.data();
}
void SharedBuffer::Append(const SharedBuffer& data) {
const char* segment;
size_t position = 0;
while (size_t length = data.GetSomeDataInternal(segment, position)) {
Append(segment, length);
position += length;
}
}
void SharedBuffer::AppendInternal(const char* data, size_t length) {
if (!length)
return;
DCHECK_GE(size_, buffer_.size());
size_t position_in_segment = OffsetInSegment(size_ - buffer_.size());
size_ += length;
if (size_ <= kSegmentSize) {
// No need to use segments for small resource data.
buffer_.Append(data, length);
return;
}
char* segment;
if (!position_in_segment) {
segment = AllocateSegment();
segments_.push_back(segment);
} else
segment = segments_.back() + position_in_segment;
size_t segment_free_space = kSegmentSize - position_in_segment;
size_t bytes_to_copy = std::min(length, segment_free_space);
for (;;) {
memcpy(segment, data, bytes_to_copy);
if (length == bytes_to_copy)
break;
length -= bytes_to_copy;
data += bytes_to_copy;
segment = AllocateSegment();
segments_.push_back(segment);
bytes_to_copy = std::min(length, static_cast<size_t>(kSegmentSize));
}
}
void SharedBuffer::Append(const Vector<char>& data) {
Append(data.data(), data.size());
}
void SharedBuffer::Clear() {
for (size_t i = 0; i < segments_.size(); ++i)
FreeSegment(segments_[i]);
segments_.clear();
size_ = 0;
buffer_.clear();
}
Vector<char> SharedBuffer::Copy() const {
Vector<char> buffer;
buffer.ReserveInitialCapacity(size_);
ForEachSegment([&buffer](const char* segment, size_t segment_size,
size_t segment_offset) -> bool {
buffer.Append(segment, segment_size);
return true;
});
DCHECK_EQ(buffer.size(), size_);
return buffer;
}
void SharedBuffer::MergeSegmentsIntoBuffer() const {
size_t buffer_size = buffer_.size();
if (size_ > buffer_size) {
size_t bytes_left = size_ - buffer_size;
for (size_t i = 0; i < segments_.size(); ++i) {
size_t bytes_to_copy =
std::min(bytes_left, static_cast<size_t>(kSegmentSize));
buffer_.Append(segments_[i], bytes_to_copy);
bytes_left -= bytes_to_copy;
FreeSegment(segments_[i]);
}
segments_.clear();
}
}
size_t SharedBuffer::GetSomeDataInternal(const char*& some_data,
size_t position) const {
size_t total_size = size();
if (position >= total_size) {
some_data = nullptr;
return 0;
}
SECURITY_DCHECK(position < size_);
size_t consecutive_size = buffer_.size();
if (position < consecutive_size) {
some_data = buffer_.data() + position;
return consecutive_size - position;
}
position -= consecutive_size;
size_t segments = segments_.size();
size_t max_segmented_size = segments * kSegmentSize;
size_t segment = SegmentIndex(position);
if (segment < segments) {
size_t bytes_left = total_size - consecutive_size;
size_t segmented_size = std::min(max_segmented_size, bytes_left);
size_t position_in_segment = OffsetInSegment(position);
some_data = segments_[segment] + position_in_segment;
return segment == segments - 1 ? segmented_size - position
: kSegmentSize - position_in_segment;
}
NOTREACHED();
return 0;
}
bool SharedBuffer::GetBytesInternal(void* dest, size_t byte_length) const {
if (!dest)
return false;
const char* segment = nullptr;
size_t load_position = 0;
size_t write_position = 0;
while (byte_length > 0) {
size_t load_size = GetSomeDataInternal(segment, load_position);
if (load_size == 0)
break;
if (byte_length < load_size)
load_size = byte_length;
memcpy(static_cast<char*>(dest) + write_position, segment, load_size);
load_position += load_size;
write_position += load_size;
byte_length -= load_size;
}
return byte_length == 0;
}
sk_sp<SkData> SharedBuffer::GetAsSkData() const {
size_t buffer_length = size();
sk_sp<SkData> data = SkData::MakeUninitialized(buffer_length);
char* buffer = static_cast<char*>(data->writable_data());
const char* segment = nullptr;
size_t position = 0;
while (size_t segment_size = GetSomeDataInternal(segment, position)) {
memcpy(buffer + position, segment, segment_size);
position += segment_size;
}
if (position != buffer_length) {
NOTREACHED();
// Don't return the incomplete SkData.
return nullptr;
}
return data;
}
void SharedBuffer::OnMemoryDump(const String& dump_prefix,
WebProcessMemoryDump* memory_dump) const {
if (buffer_.size()) {
WebMemoryAllocatorDump* dump =
memory_dump->CreateMemoryAllocatorDump(dump_prefix + "/shared_buffer");
dump->AddScalar("size", "bytes", buffer_.size());
memory_dump->AddSuballocation(
dump->Guid(), String(WTF::Partitions::kAllocatedObjectPoolName));
} else {
// If there is data in the segments, then it should have been allocated
// using fastMalloc.
const String data_dump_name = dump_prefix + "/segments";
auto dump = memory_dump->CreateMemoryAllocatorDump(data_dump_name);
dump->AddScalar("size", "bytes", size_);
memory_dump->AddSuballocation(
dump->Guid(), String(WTF::Partitions::kAllocatedObjectPoolName));
}
}
SharedBuffer::DeprecatedFlatData::DeprecatedFlatData(
scoped_refptr<const SharedBuffer> buffer)
: buffer_(std::move(buffer)) {
DCHECK(buffer_);
if (buffer_->size() <= buffer_->buffer_.size()) {
// The SharedBuffer is not segmented - just point to its data.
data_ = buffer_->buffer_.data();
return;
}
// Merge all segments.
flat_buffer_.ReserveInitialCapacity(buffer_->size());
buffer_->ForEachSegment([this](const char* segment, size_t segment_size,
size_t segment_offset) -> bool {
flat_buffer_.Append(segment, segment_size);
return true;
});
data_ = flat_buffer_.data();
}
} // namespace blink