blob: e6951edd0c6e2088be355bfb74a9527ab3e970eb [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/platform/image-decoders/segment_reader.h"
#include <utility>
#include "base/containers/span.h"
#include "base/memory/scoped_refptr.h"
#include "third_party/blink/renderer/platform/graphics/parkable_image.h"
#include "third_party/blink/renderer/platform/graphics/rw_buffer.h"
#include "third_party/blink/renderer/platform/wtf/shared_buffer.h"
#include "third_party/blink/renderer/platform/wtf/threading_primitives.h"
#include "third_party/skia/include/core/SkData.h"
namespace blink {
namespace {
// Helpers for ROBufferSegmentReader and ParkableImageSegmentReader
template <class Iter>
size_t BufferGetSomeData(Iter& iter,
size_t& position_of_block,
const char*& data,
size_t position) {
for (size_t size_of_block = iter.size(); size_of_block != 0;
position_of_block += size_of_block, size_of_block = iter.size()) {
DCHECK_LE(position_of_block, position);
if (position_of_block + size_of_block > position) {
// |position| is in this block.
const size_t position_in_block = position - position_of_block;
data = static_cast<const char*>(iter.data()) + position_in_block;
return size_of_block - position_in_block;
}
// Move to next block.
if (!iter.Next())
break;
}
return 0;
}
template <class Iter>
sk_sp<SkData> BufferCopyAsSkData(Iter iter, size_t available) {
sk_sp<SkData> data = SkData::MakeUninitialized(available);
char* dst = static_cast<char*>(data->writable_data());
do {
size_t size = iter.size();
memcpy(dst, iter.data(), size);
dst += size;
} while (iter.Next());
return data;
}
} // namespace
// SharedBufferSegmentReader ---------------------------------------------------
// Interface for ImageDecoder to read a SharedBuffer.
class SharedBufferSegmentReader final : public SegmentReader {
public:
explicit SharedBufferSegmentReader(scoped_refptr<SharedBuffer>);
SharedBufferSegmentReader(const SharedBufferSegmentReader&) = delete;
SharedBufferSegmentReader& operator=(const SharedBufferSegmentReader&) =
delete;
size_t size() const override;
size_t GetSomeData(const char*& data, size_t position) const override;
sk_sp<SkData> GetAsSkData() const override;
private:
scoped_refptr<SharedBuffer> shared_buffer_;
};
SharedBufferSegmentReader::SharedBufferSegmentReader(
scoped_refptr<SharedBuffer> buffer)
: shared_buffer_(std::move(buffer)) {}
size_t SharedBufferSegmentReader::size() const {
return shared_buffer_->size();
}
size_t SharedBufferSegmentReader::GetSomeData(const char*& data,
size_t position) const {
data = nullptr;
auto it = shared_buffer_->GetIteratorAt(position);
if (it == shared_buffer_->cend())
return 0;
data = it->data();
return it->size();
}
sk_sp<SkData> SharedBufferSegmentReader::GetAsSkData() const {
sk_sp<SkData> data = SkData::MakeUninitialized(shared_buffer_->size());
char* buffer = static_cast<char*>(data->writable_data());
size_t offset = 0;
for (const auto& span : *shared_buffer_) {
memcpy(buffer + offset, span.data(), span.size());
offset += span.size();
}
return data;
}
// DataSegmentReader -----------------------------------------------------------
// Interface for ImageDecoder to read an SkData.
class DataSegmentReader final : public SegmentReader {
public:
explicit DataSegmentReader(sk_sp<SkData>);
DataSegmentReader(const DataSegmentReader&) = delete;
DataSegmentReader& operator=(const DataSegmentReader&) = delete;
size_t size() const override;
size_t GetSomeData(const char*& data, size_t position) const override;
sk_sp<SkData> GetAsSkData() const override;
private:
sk_sp<SkData> data_;
};
DataSegmentReader::DataSegmentReader(sk_sp<SkData> data)
: data_(std::move(data)) {}
size_t DataSegmentReader::size() const {
return data_->size();
}
size_t DataSegmentReader::GetSomeData(const char*& data,
size_t position) const {
if (position >= data_->size())
return 0;
data = reinterpret_cast<const char*>(data_->bytes() + position);
return data_->size() - position;
}
sk_sp<SkData> DataSegmentReader::GetAsSkData() const {
return data_;
}
// ROBufferSegmentReader -------------------------------------------------------
class ROBufferSegmentReader final : public SegmentReader {
public:
explicit ROBufferSegmentReader(scoped_refptr<ROBuffer>);
ROBufferSegmentReader(const ROBufferSegmentReader&) = delete;
ROBufferSegmentReader& operator=(const ROBufferSegmentReader&) = delete;
size_t size() const override;
size_t GetSomeData(const char*& data, size_t position) const override;
sk_sp<SkData> GetAsSkData() const override;
private:
scoped_refptr<ROBuffer> ro_buffer_;
mutable Mutex read_mutex_;
// Position of the first char in the current block of iter_.
mutable size_t position_of_block_ GUARDED_BY(read_mutex_);
mutable ROBuffer::Iter iter_ GUARDED_BY(read_mutex_);
};
ROBufferSegmentReader::ROBufferSegmentReader(scoped_refptr<ROBuffer> buffer)
: ro_buffer_(std::move(buffer)),
position_of_block_(0),
iter_(ro_buffer_.get()) {}
size_t ROBufferSegmentReader::size() const {
return ro_buffer_ ? ro_buffer_->size() : 0;
}
size_t ROBufferSegmentReader::GetSomeData(const char*& data,
size_t position) const {
if (!ro_buffer_)
return 0;
MutexLocker lock(read_mutex_);
if (position < position_of_block_) {
// ROBuffer::Iter only iterates forwards. Start from the beginning.
iter_.Reset(ro_buffer_.get());
position_of_block_ = 0;
}
size_t size = BufferGetSomeData(iter_, position_of_block_, data, position);
if (!iter_.data()) {
// Reset to the beginning, so future calls can succeed.
iter_.Reset(ro_buffer_.get());
position_of_block_ = 0;
}
return size;
}
static void UnrefROBuffer(const void* ptr, void* context) {
static_cast<ROBuffer*>(context)->Release();
}
sk_sp<SkData> ROBufferSegmentReader::GetAsSkData() const {
if (!ro_buffer_)
return nullptr;
// Check to see if the data is already contiguous.
ROBuffer::Iter iter(ro_buffer_.get());
const bool multiple_blocks = iter.Next();
iter.Reset(ro_buffer_.get());
if (!multiple_blocks) {
// Contiguous data. No need to copy.
ro_buffer_->AddRef();
return SkData::MakeWithProc(iter.data(), iter.size(), &UnrefROBuffer,
ro_buffer_.get());
}
return BufferCopyAsSkData(iter, ro_buffer_->size());
}
// ParkableImageSegmentReader
class ParkableImageSegmentReader : public SegmentReader {
public:
explicit ParkableImageSegmentReader(scoped_refptr<ParkableImage> image);
~ParkableImageSegmentReader() override = default;
size_t size() const override;
size_t GetSomeData(const char*& data, size_t position) const override;
sk_sp<SkData> GetAsSkData() const override;
void LockData() override;
void UnlockData() override;
private:
scoped_refptr<ParkableImage> parkable_image_;
size_t available_;
};
ParkableImageSegmentReader::ParkableImageSegmentReader(
scoped_refptr<ParkableImage> image)
: parkable_image_(std::move(image)), available_(parkable_image_->size()) {
}
size_t ParkableImageSegmentReader::size() const {
return available_;
}
size_t ParkableImageSegmentReader::GetSomeData(const char*& data,
size_t position) const {
if (!parkable_image_)
return 0;
MutexLocker lock(parkable_image_->impl_->lock_);
DCHECK(parkable_image_->impl_->is_locked());
RWBuffer::ROIter iter(parkable_image_->impl_->rw_buffer_.get(), available_);
size_t position_of_block = 0;
return BufferGetSomeData(iter, position_of_block, data, position);
}
sk_sp<SkData> ParkableImageSegmentReader::GetAsSkData() const {
if (!parkable_image_)
return nullptr;
MutexLocker lock(parkable_image_->impl_->lock_);
parkable_image_->impl_->Unpark();
RWBuffer::ROIter iter(parkable_image_->impl_->rw_buffer_.get(), available_);
if (!iter.HasNext()) { // No need to copy because the data is contiguous.
// We lock here so that we don't get a use-after-free. ParkableImage can
// not be parked while it is locked, so the buffer is valid for the whole
// lifetime of the SkData. We add the ref so that the ParkableImage has a
// longer limetime than the SkData.
parkable_image_->AddRef();
parkable_image_->LockData();
return SkData::MakeWithProc(
iter.data(), available_,
[](const void* ptr, void* context) -> void {
auto* parkable_image = static_cast<ParkableImage*>(context);
{
MutexLocker lock(parkable_image->impl_->lock_);
parkable_image->UnlockData();
}
// Don't hold the mutex while we call |Release|, since |Release| can
// free the ParkableImage, if this is the last reference to it;
// Freeing the ParkableImage while the mutex is held causes a UAF when
// the dtor for MutexLocker is called.
parkable_image->Release();
},
parkable_image_.get());
}
// Data is not contiguous so we need to copy.
return BufferCopyAsSkData(iter, available_);
}
void ParkableImageSegmentReader::LockData() {
MutexLocker lock(parkable_image_->impl_->lock_);
parkable_image_->impl_->Unpark();
parkable_image_->LockData();
}
void ParkableImageSegmentReader::UnlockData() {
MutexLocker lock(parkable_image_->impl_->lock_);
parkable_image_->UnlockData();
}
// SegmentReader ---------------------------------------------------------------
scoped_refptr<SegmentReader> SegmentReader::CreateFromSharedBuffer(
scoped_refptr<SharedBuffer> buffer) {
return base::AdoptRef(new SharedBufferSegmentReader(std::move(buffer)));
}
scoped_refptr<SegmentReader> SegmentReader::CreateFromSkData(
sk_sp<SkData> data) {
return base::AdoptRef(new DataSegmentReader(std::move(data)));
}
scoped_refptr<SegmentReader> SegmentReader::CreateFromROBuffer(
scoped_refptr<ROBuffer> buffer) {
return base::AdoptRef(new ROBufferSegmentReader(std::move(buffer)));
}
scoped_refptr<SegmentReader> SegmentReader::CreateFromParkableImage(
scoped_refptr<ParkableImage> image) {
return base::AdoptRef(new ParkableImageSegmentReader(std::move(image)));
}
} // namespace blink