blob: 88e8b46dbe62ee4226b03aad6640bf6b6c5704fc [file]
// Copyright 2006 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/sessions/core/session_command.h"
#include <algorithm>
#include <limits>
#include <memory>
#include "base/compiler_specific.h"
#include "base/containers/span.h"
#include "base/logging.h"
#include "base/numerics/byte_conversions.h"
#include "base/pickle.h"
namespace sessions {
SessionCommand::SessionCommand(id_type id, size_type size)
: id_(id), contents_(size, 0) {
// Note that it is possible for size to be greater than kMaxContentSize.
// This is allowed for historical reasons, the contents will be truncated when
// Serialize() is called.
}
SessionCommand::SessionCommand(id_type id, const base::Pickle& pickle)
: id_(id), contents_(pickle.size(), 0) {
DCHECK(pickle.size() < kMaxContentSize);
contents().copy_from(pickle);
}
bool SessionCommand::GetContents(void* dest, size_t count) const {
if (contents_.size() != count) {
return false;
}
UNSAFE_TODO(memcpy(dest, &(contents_[0]), count));
return true;
}
base::PickleIterator SessionCommand::ContentsAsPickle() const {
return base::PickleIterator::WithData(contents());
}
std::vector<uint8_t> SessionCommand::Serialize() const {
if (contents().size() > kMaxContentSize) {
VLOG(2) << "SessionCommand::Serialize: contents_size " << contents().size()
<< " is greater than kMaxContentSize " << kMaxContentSize
<< " and will be truncated.";
}
const size_type contents_size = std::min(contents().size(), kMaxContentSize);
// Note that total_size can be greater that UINT16_MAX, so we use size_t
// instead of size_type.
const size_type size_field_value = sizeof(id_type) + contents_size;
const size_t total_size = sizeof(size_type) + size_field_value;
std::vector<uint8_t> result(total_size);
base::span<uint8_t> remaining = base::span(result);
remaining.take_first(sizeof(size_type))
.copy_from(base::U16ToNativeEndian(size_field_value));
remaining.take_first(sizeof(id_type))
.copy_from(base::byte_span_from_ref(id()));
// This is where truncation of contents_ can occur.
remaining.copy_from(contents().first(contents_size));
return result;
}
std::optional<size_t> SessionCommand::GetSerializedSize(
base::span<const uint8_t> data) {
if (data.size() < sizeof(size_type)) {
// If there's just one byte of data, then it's ignored and not an error.
return std::nullopt;
}
return sizeof(size_type) +
base::U16FromNativeEndian(data.first<sizeof(size_type)>());
}
std::unique_ptr<SessionCommand> SessionCommand::Deserialize(
base::span<const uint8_t> data) {
if (data.size() < sizeof(size_type)) {
VLOG(2) << "SessionCommand::Deserialize: data.size() " << data.size()
<< " is less than sizeof(size_type) " << sizeof(size_type);
return nullptr;
}
base::span<const uint8_t> remaining = data;
// Parse the size field.
const size_type size_field_value =
base::U16FromNativeEndian(remaining.take_first<sizeof(size_type)>());
if (remaining.size() < size_field_value) {
VLOG(2) << "SessionCommand::Deserialize: remaining.size() "
<< remaining.size() << " is less than size_field_value "
<< size_field_value;
return nullptr;
}
if (size_field_value < sizeof(id_type)) {
VLOG(2) << "SessionCommand::Deserialize: size_field_value "
<< size_field_value << " is less than sizeof(id_type) "
<< sizeof(id_type);
return nullptr;
}
// Parse the id field.
const id_type command_id = remaining.take_first<sizeof(id_type)>()[0];
// Parse the contents field.
const size_type content_size = size_field_value - sizeof(id_type);
std::unique_ptr<sessions::SessionCommand> command =
std::make_unique<sessions::SessionCommand>(
command_id, static_cast<size_type>(content_size));
if (content_size > 0) {
command->contents().copy_from(remaining.take_first(content_size));
}
return command;
}
} // namespace sessions