blob: c8eff3d005b1c6075fc0825e380476a8b7242bd7 [file] [log] [blame]
// Copyright 2025 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/dbus/utils/variant.h"
#include "base/check.h"
#include "base/check_op.h"
#include "base/notreached.h"
#include "components/dbus/utils/signature.h"
#include "third_party/abseil-cpp/absl/functional/overload.h"
namespace dbus_utils {
Variant::Variant() : state_(std::monostate{}) {}
Variant::Variant(Variant&& other) noexcept
: signature_(std::move(other.signature_)), state_(std::move(other.state_)) {
other.state_.emplace<std::monostate>();
}
Variant& Variant::operator=(Variant&& other) noexcept {
if (this != &other) {
signature_ = std::move(other.signature_);
state_ = std::move(other.state_);
other.state_.emplace<std::monostate>();
}
return *this;
}
Variant::~Variant() = default;
void Variant::Write(dbus::MessageWriter& writer) const {
dbus::MessageWriter variant_writer(nullptr);
writer.OpenVariant(signature_, &variant_writer);
WriteContent(variant_writer);
writer.CloseContainer(&variant_writer);
}
void Variant::WriteContent(dbus::MessageWriter& writer) const {
std::visit(absl::Overload{
[&](const std::monostate&) { NOTREACHED(); },
[&](const bool& v) { writer.AppendBool(v); },
[&](const uint8_t& v) { writer.AppendByte(v); },
[&](const int16_t& v) { writer.AppendInt16(v); },
[&](const uint16_t& v) { writer.AppendUint16(v); },
[&](const int32_t& v) { writer.AppendInt32(v); },
[&](const uint32_t& v) { writer.AppendUint32(v); },
[&](const int64_t& v) { writer.AppendInt64(v); },
[&](const uint64_t& v) { writer.AppendUint64(v); },
[&](const double& v) { writer.AppendDouble(v); },
[&](const std::string& v) { writer.AppendString(v); },
[&](const dbus::ObjectPath& v) { writer.AppendObjectPath(v); },
[&](const base::ScopedFD& v) {
writer.AppendFileDescriptor(v.get());
},
[&](const Dictionary& v) {
std::string member_sig = signature_.substr(1);
dbus::MessageWriter array_writer(nullptr);
writer.OpenArray(member_sig.c_str(), &array_writer);
for (const auto& pair : v) {
dbus::MessageWriter entry_writer(nullptr);
array_writer.OpenDictEntry(&entry_writer);
pair.first.WriteContent(entry_writer);
pair.second.WriteContent(entry_writer);
array_writer.CloseContainer(&entry_writer);
}
writer.CloseContainer(&array_writer);
},
[&](const Sequence& v) {
CHECK(!signature_.empty());
if (signature_[0] == DBUS_STRUCT_BEGIN_CHAR) {
dbus::MessageWriter struct_writer(nullptr);
writer.OpenStruct(&struct_writer);
for (const auto& member : v) {
member.WriteContent(struct_writer);
}
writer.CloseContainer(&struct_writer);
} else {
CHECK_EQ(signature_[0], DBUS_TYPE_ARRAY);
dbus::MessageWriter array_writer(nullptr);
writer.OpenArray(signature_.substr(1), &array_writer);
for (const auto& element : v) {
element.WriteContent(array_writer);
}
writer.CloseContainer(&array_writer);
}
},
[&](const NestedVariant& v) { v->Write(writer); }},
state_);
}
bool Variant::Read(dbus::MessageReader& reader) {
dbus::MessageReader variant_reader(nullptr);
if (!reader.PopVariant(&variant_reader)) {
return false;
}
std::string signature = variant_reader.GetDataSignature();
if (!ReadContent(variant_reader) || variant_reader.HasMoreData()) {
return false;
}
signature_ = std::move(signature);
return true;
}
bool Variant::ReadContent(dbus::MessageReader& reader) {
auto read_primitive =
[&]<typename T>(bool (dbus::MessageReader::*pop)(T*)) -> bool {
T v;
if (!(reader.*pop)(&v)) {
return false;
}
state_.emplace<T>(std::move(v));
return true;
};
switch (reader.GetDataType()) {
case dbus::Message::DataType::BOOL:
return read_primitive(&dbus::MessageReader::PopBool);
case dbus::Message::DataType::BYTE:
return read_primitive(&dbus::MessageReader::PopByte);
case dbus::Message::DataType::INT16:
return read_primitive(&dbus::MessageReader::PopInt16);
case dbus::Message::DataType::UINT16:
return read_primitive(&dbus::MessageReader::PopUint16);
case dbus::Message::DataType::INT32:
return read_primitive(&dbus::MessageReader::PopInt32);
case dbus::Message::DataType::UINT32:
return read_primitive(&dbus::MessageReader::PopUint32);
case dbus::Message::DataType::INT64:
return read_primitive(&dbus::MessageReader::PopInt64);
case dbus::Message::DataType::UINT64:
return read_primitive(&dbus::MessageReader::PopUint64);
case dbus::Message::DataType::DOUBLE:
return read_primitive(&dbus::MessageReader::PopDouble);
case dbus::Message::DataType::STRING:
return read_primitive(&dbus::MessageReader::PopString);
case dbus::Message::DataType::OBJECT_PATH:
return read_primitive(&dbus::MessageReader::PopObjectPath);
case dbus::Message::DataType::UNIX_FD:
return read_primitive(&dbus::MessageReader::PopFileDescriptor);
case dbus::Message::DataType::VARIANT: {
auto nested_variant = std::make_unique<Variant>();
if (!nested_variant->Read(reader)) {
return false;
}
state_.emplace<NestedVariant>(std::move(nested_variant));
return true;
}
case dbus::Message::DataType::ARRAY: {
std::string reader_signature = reader.GetDataSignature();
if (reader_signature.length() > 1 &&
reader_signature[1] == DBUS_DICT_ENTRY_BEGIN_CHAR) {
// Dictionary
dbus::MessageReader array_reader(nullptr);
if (!reader.PopArray(&array_reader)) {
return false;
}
Dictionary dict_data;
while (array_reader.HasMoreData()) {
dbus::MessageReader entry_reader(nullptr);
if (!array_reader.PopDictEntry(&entry_reader)) {
return false;
}
Variant key, value;
std::string key_signature = entry_reader.GetDataSignature();
if (!key.ReadContent(entry_reader) || !entry_reader.HasMoreData()) {
return false;
}
key.signature_ = std::move(key_signature);
std::string value_signature = entry_reader.GetDataSignature();
if (!value.ReadContent(entry_reader) || entry_reader.HasMoreData()) {
return false;
}
value.signature_ = std::move(value_signature);
dict_data.emplace_back(std::move(key), std::move(value));
}
state_.emplace<Dictionary>(std::move(dict_data));
return true;
} else {
// Array
dbus::MessageReader array_reader(nullptr);
if (!reader.PopArray(&array_reader)) {
return false;
}
Sequence seq_data;
while (array_reader.HasMoreData()) {
Variant element;
std::string element_signature = array_reader.GetDataSignature();
if (!element.ReadContent(array_reader)) {
return false;
}
element.signature_ = std::move(element_signature);
seq_data.push_back(std::move(element));
}
state_.emplace<Sequence>(std::move(seq_data));
return true;
}
}
case dbus::Message::DataType::STRUCT: {
dbus::MessageReader struct_reader(nullptr);
if (!reader.PopStruct(&struct_reader)) {
return false;
}
Sequence seq_data;
while (struct_reader.HasMoreData()) {
Variant member;
std::string member_signature = struct_reader.GetDataSignature();
if (!member.ReadContent(struct_reader)) {
return false;
}
member.signature_ = std::move(member_signature);
seq_data.push_back(std::move(member));
}
state_.emplace<Sequence>(std::move(seq_data));
return true;
}
case dbus::Message::DataType::DICT_ENTRY:
case dbus::Message::DataType::INVALID_DATA:
return false;
}
return false;
}
} // namespace dbus_utils