blob: 9971ec73a6196938c9c36f705a52513f45cfe70b [file] [log] [blame] [edit]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SRC_DEVELOPER_DEBUG_IPC_MESSAGE_WRITER_H_
#define SRC_DEVELOPER_DEBUG_IPC_MESSAGE_WRITER_H_
#include <stdint.h>
#include <cstdint>
#include <vector>
#include "src/developer/debug/ipc/protocol.h"
#include "src/developer/debug/shared/serialization.h"
namespace debug_ipc {
// Provides a simple means to append to a dynamic buffer different types of
// data.
//
// The first 4 bytes of each message is the message size. It's assumed that
// these bytes will be explicitly written to. Normally a message will start
// with a struct which contains space for this explicitly.
class MessageWriter : public Serializer {
public:
// |initial_size| is a hint for the initial size of the message.
MessageWriter(uint32_t version, size_t initial_size) : version_(version) {
buffer_.reserve(initial_size);
}
size_t current_length() const { return buffer_.size(); }
// Writes the size of the current buffer to the first 4 bytes, and
// destructively returns the buffer.
std::vector<char> MessageComplete();
// Implement |Serializer|.
uint32_t GetVersion() const override { return version_; }
void SerializeBytes(void* data, uint32_t len) override;
private:
uint32_t version_;
std::vector<char> buffer_;
};
namespace internal {
template <typename MsgType>
bool IsSupported(uint32_t version)
requires requires(MsgType t) {
{ t.kSupportedSinceVersion };
}
{
return version >= MsgType::kSupportedSinceVersion;
}
// The message types that were part of the protocol before we supported different versions do not
// have the kSupportedSinceVersion field and are supported by all protocol versions.
template <typename>
bool IsSupported(...) {
return true;
}
template <typename MsgType>
constexpr MsgHeader::Type GetTypeForMsg(const MsgType& msg) {
#define FN(msg_type) \
if constexpr (std::same_as<MsgType, msg_type##Request> || \
std::same_as<MsgType, msg_type##Reply>) { \
return MsgHeader::Type::k##msg_type; \
}
FOR_EACH_REQUEST_TYPE(FN)
#undef FN
#define FN(notification_type) \
if constexpr (std::same_as<MsgType, notification_type>) { \
return MsgHeader::Type::k##notification_type; \
}
FOR_EACH_NOTIFICATION_TYPE(FN)
#undef FN
return MsgHeader::Type::kNone;
}
} // namespace internal
template <typename MsgType>
std::vector<char> Serialize(const MsgType& request, uint32_t transaction_id, uint32_t version)
requires IsDebugIpcMessageType<MsgType>
{
if (!internal::IsSupported<MsgType>(version)) {
return {};
}
MsgHeader header{0, internal::GetTypeForMsg(request), transaction_id};
MessageWriter writer(version, sizeof(header) + sizeof(request));
writer | header | const_cast<MsgType&>(request);
return writer.MessageComplete();
}
template <typename NotificationType>
std::vector<char> Serialize(const NotificationType& notify, uint32_t version)
requires IsDebugIpcNotificationType<NotificationType>
{
if (!internal::IsSupported<NotificationType>(version)) {
return {};
}
MsgHeader header{0, internal::GetTypeForMsg(notify), 0};
MessageWriter writer(version, sizeof(header) + sizeof(notify));
writer | header | const_cast<NotificationType&>(notify);
return writer.MessageComplete();
}
} // namespace debug_ipc
#endif // SRC_DEVELOPER_DEBUG_IPC_MESSAGE_WRITER_H_