// Copyright 2014 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 "chromecast/media/cma/ipc/media_message.h"
#include <limits>
#include "base/basictypes.h"
#include "base/logging.h"
#include "chromecast/media/cma/ipc/media_memory_chunk.h"
namespace chromecast {
namespace media {
// static
scoped_ptr<MediaMessage> MediaMessage::CreateDummyMessage(
uint32 type) {
return scoped_ptr<MediaMessage>(
new MediaMessage(type, std::numeric_limits<size_t>::max()));
}
// static
scoped_ptr<MediaMessage> MediaMessage::CreateMessage(
uint32 type,
const MemoryAllocatorCB& memory_allocator,
size_t msg_content_capacity) {
size_t msg_size = minimum_msg_size() + msg_content_capacity;
// Make the message size a multiple of the alignment
// so that if we have proper alignment for array of messages.
size_t end_alignment = msg_size % ALIGNOF(SerializedMsg);
if (end_alignment != 0)
msg_size += ALIGNOF(SerializedMsg) - end_alignment;
scoped_ptr<MediaMemoryChunk> memory(memory_allocator.Run(msg_size));
if (!memory)
return scoped_ptr<MediaMessage>();
return scoped_ptr<MediaMessage>(new MediaMessage(type, memory.Pass()));
}
// static
scoped_ptr<MediaMessage> MediaMessage::CreateMessage(
uint32 type,
scoped_ptr<MediaMemoryChunk> memory) {
return scoped_ptr<MediaMessage>(new MediaMessage(type, memory.Pass()));
}
// static
scoped_ptr<MediaMessage> MediaMessage::MapMessage(
scoped_ptr<MediaMemoryChunk> memory) {
return scoped_ptr<MediaMessage>(new MediaMessage(memory.Pass()));
}
MediaMessage::MediaMessage(uint32 type, size_t msg_size)
: is_dummy_msg_(true),
cached_header_(&cached_msg_.header),
msg_(&cached_msg_),
msg_read_only_(&cached_msg_),
rd_offset_(0) {
cached_header_->size = msg_size;
cached_header_->type = type;
cached_header_->content_size = 0;
}
MediaMessage::MediaMessage(uint32 type, scoped_ptr<MediaMemoryChunk> memory)
: is_dummy_msg_(false),
cached_header_(&cached_msg_.header),
msg_(static_cast<SerializedMsg*>(memory->data())),
msg_read_only_(msg_),
mem_(memory.Pass()),
rd_offset_(0) {
CHECK(mem_->valid());
CHECK_GE(mem_->size(), minimum_msg_size());
// Check memory alignment:
// needed to cast properly |msg_dst| to a SerializedMsg.
CHECK_EQ(
reinterpret_cast<uintptr_t>(mem_->data()) % ALIGNOF(SerializedMsg), 0u);
// Make sure that |mem_->data()| + |mem_->size()| is also aligned correctly.
// This is needed if we append a second serialized message next to this one.
// The second serialized message must be aligned correctly.
// It is similar to what a compiler is doing for arrays of structures.
CHECK_EQ(mem_->size() % ALIGNOF(SerializedMsg), 0u);
cached_header_->size = mem_->size();
cached_header_->type = type;
cached_header_->content_size = 0;
msg_->header = *cached_header_;
}
MediaMessage::MediaMessage(scoped_ptr<MediaMemoryChunk> memory)
: is_dummy_msg_(false),
cached_header_(&cached_msg_.header),
msg_(NULL),
msg_read_only_(static_cast<SerializedMsg*>(memory->data())),
mem_(memory.Pass()),
rd_offset_(0) {
CHECK(mem_->valid());
// Check memory alignment.
CHECK_EQ(
reinterpret_cast<uintptr_t>(mem_->data()) % ALIGNOF(SerializedMsg), 0u);
// Cache the message header which cannot be modified while reading.
CHECK_GE(mem_->size(), minimum_msg_size());
*cached_header_ = msg_read_only_->header;
CHECK_GE(cached_header_->size, minimum_msg_size());
// Make sure if we have 2 consecutive serialized messages in memory,
// the 2nd message is also aligned correctly.
CHECK_EQ(cached_header_->size % ALIGNOF(SerializedMsg), 0u);
size_t max_content_size = cached_header_->size - minimum_msg_size();
CHECK_LE(cached_header_->content_size, max_content_size);
}
MediaMessage::~MediaMessage() {
}
bool MediaMessage::IsSerializedMsgAvailable() const {
return !is_dummy_msg_ && mem_->valid();
}
bool MediaMessage::WriteBuffer(const void* src, size_t size) {
// No message to write into.
if (!msg_)
return false;
// The underlying memory was invalidated.
if (!is_dummy_msg_ && !mem_->valid())
return false;
size_t max_content_size = cached_header_->size - minimum_msg_size();
if (cached_header_->content_size + size > max_content_size) {
cached_header_->content_size = max_content_size;
msg_->header.content_size = cached_header_->content_size;
return false;
}
// Write the message only for non-dummy messages.
if (!is_dummy_msg_) {
uint8* wr_ptr = &msg_->content + cached_header_->content_size;
memcpy(wr_ptr, src, size);
}
cached_header_->content_size += size;
msg_->header.content_size = cached_header_->content_size;
return true;
}
bool MediaMessage::ReadBuffer(void* dst, size_t size) {
// No read possible for a dummy message.
if (is_dummy_msg_)
return false;
// The underlying memory was invalidated.
if (!mem_->valid())
return false;
if (rd_offset_ + size > cached_header_->content_size) {
rd_offset_ = cached_header_->content_size;
return false;
}
const uint8* rd_ptr = &msg_read_only_->content + rd_offset_;
memcpy(dst, rd_ptr, size);
rd_offset_ += size;
return true;
}
void* MediaMessage::GetWritableBuffer(size_t size) {
// No read possible for a dummy message.
if (is_dummy_msg_)
return NULL;
// The underlying memory was invalidated.
if (!mem_->valid())
return NULL;
if (rd_offset_ + size > cached_header_->content_size) {
rd_offset_ = cached_header_->content_size;
return NULL;
}
uint8* rd_ptr = &msg_read_only_->content + rd_offset_;
rd_offset_ += size;
return rd_ptr;
}
const void* MediaMessage::GetBuffer(size_t size) {
return GetWritableBuffer(size);
}
} // namespace media
} // namespace chromecast