#include "remoting/host/linux/gvariant_ref.h"
#include <glib.h>
#include <glibconfig.h>
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <string>
#include <string_view>
#include <tuple>
#include <utility>
#include "base/check.h"
#include "base/compiler_specific.h"
#include "base/location.h"
#include "base/strings/strcat.h"
#include "base/types/expected.h"
#include "remoting/host/base/loggable.h"
#include "remoting/host/linux/gvariant_type.h"
namespace remoting::gvariant {
GVariantBase::operator GVariantRef<>() const {
return GVariantRef<>::Ref(raw());
}
GVariant* GVariantBase::raw() const {
return variant_.get();
}
GVariant* GVariantBase::release() && {
return variant_.release();
}
Type<> GVariantBase::GetType() const {
return Type(g_variant_get_type(variant_.get()));
}
GVariantBase::GVariantBase() = default;
GVariantBase::GVariantBase(GVariantPtr variant)
: variant_(std::move(variant)) {}
GVariantBase::GVariantBase(const GVariantBase& other) = default;
GVariantBase::GVariantBase(GVariantBase&& other) = default;
GVariantBase& GVariantBase::operator=(const GVariantBase& other) = default;
GVariantBase& GVariantBase::operator=(GVariantBase&& other) = default;
GVariantBase::~GVariantBase() = default;
bool operator==(const GVariantBase& lhs, const GVariantBase& rhs) {
return g_variant_equal(lhs.raw(), rhs.raw());
}
template <Type C>
GVariantRef<C> GVariantRef<C>::Take(GVariant* variant)
requires(C == Type("*"))
{
return GVariantRef<C>(GVariantPtr::Take(variant));
}
template GVariantRef<Type("*")> GVariantRef<Type("*")>::Take(GVariant* variant);
template <Type C>
GVariantRef<C> GVariantRef<C>::Ref(GVariant* variant)
requires(C == Type("*"))
{
return GVariantRef<C>(GVariantPtr::Ref(variant));
}
template GVariantRef<Type("*")> GVariantRef<Type("*")>::Ref(GVariant* variant);
template <Type C>
GVariantRef<C> GVariantRef<C>::RefSink(GVariant* variant)
requires(C == Type("*"))
{
return GVariantRef<C>(GVariantPtr::RefSink(variant));
}
template GVariantRef<Type("*")> GVariantRef<Type("*")>::RefSink(
GVariant* variant);
ObjectPathCStr::ObjectPathCStr(const ObjectPath& path LIFETIME_BOUND)
: path_(path.c_str()) {}
base::expected<ObjectPathCStr, Loggable> ObjectPathCStr::TryFrom(
const char* path LIFETIME_BOUND) {
if (g_variant_is_object_path(path)) {
return base::ok(ObjectPathCStr(path, Checked()));
} else {
return base::unexpected(
Loggable(FROM_HERE,
base::StrCat({"String is not a valid object path: ", path})));
}
}
ObjectPathCStr::ObjectPathCStr(const char* path LIFETIME_BOUND, Checked)
: path_(path) {}
ObjectPath::ObjectPath() : path_("/") {}
ObjectPath::ObjectPath(ObjectPathCStr path) : path_(path.c_str()) {}
base::expected<ObjectPath, Loggable> ObjectPath::TryFrom(std::string path) {
return ObjectPathCStr::TryFrom(path.c_str()).transform([&](ObjectPathCStr) {
return ObjectPath(std::move(path));
});
}
const std::string& ObjectPath::value() const {
return path_;
}
const char* ObjectPath::c_str() const {
return path_.c_str();
}
ObjectPath::ObjectPath(std::string path) : path_(path) {}
TypeSignatureCStr::TypeSignatureCStr(
const TypeSignature& signature LIFETIME_BOUND)
: signature_(signature.c_str()) {}
base::expected<TypeSignatureCStr, Loggable> TypeSignatureCStr::TryFrom(
const char* signature LIFETIME_BOUND) {
if (g_variant_is_signature(signature)) {
return base::ok(TypeSignatureCStr(signature, Checked()));
} else {
return base::unexpected(Loggable(
FROM_HERE,
base::StrCat({"String is not a valid type signature: ", signature})));
}
}
TypeSignatureCStr::TypeSignatureCStr(const char* signature LIFETIME_BOUND,
Checked)
: signature_(signature) {}
TypeSignature::TypeSignature() = default;
TypeSignature::TypeSignature(TypeSignatureCStr signature)
: signature_(signature.c_str()) {}
base::expected<TypeSignature, Loggable> TypeSignature::TryFrom(
std::string signature) {
return TypeSignatureCStr::TryFrom(signature.c_str())
.transform([&](TypeSignatureCStr) {
return TypeSignature(std::move(signature));
});
}
const std::string& TypeSignature::value() const {
return signature_;
}
const char* TypeSignature::c_str() const {
return signature_.c_str();
}
TypeSignature::TypeSignature(std::string path) : signature_(path) {}
auto Mapping<bool>::From(bool value) -> GVariantRef<kType> {
return GVariantRef<kType>::TakeUnchecked(g_variant_new_boolean(value));
}
bool Mapping<bool>::Into(const GVariantRef<kType>& variant) {
return g_variant_get_boolean(variant.raw());
}
auto Mapping<std::uint8_t>::From(std::uint8_t value) -> GVariantRef<kType> {
return GVariantRef<kType>::TakeUnchecked(g_variant_new_byte(value));
}
std::uint8_t Mapping<std::uint8_t>::Into(const GVariantRef<kType>& variant) {
return g_variant_get_byte(variant.raw());
}
auto Mapping<std::int16_t>::From(std::int16_t value) -> GVariantRef<kType> {
return GVariantRef<kType>::TakeUnchecked(g_variant_new_int16(value));
}
std::int16_t Mapping<std::int16_t>::Into(const GVariantRef<kType>& variant) {
return g_variant_get_int16(variant.raw());
}
auto Mapping<std::uint16_t>::From(std::uint16_t value) -> GVariantRef<kType> {
return GVariantRef<kType>::TakeUnchecked(g_variant_new_uint16(value));
}
std::uint16_t Mapping<std::uint16_t>::Into(const GVariantRef<kType>& variant) {
return g_variant_get_uint16(variant.raw());
}
auto Mapping<std::int32_t>::From(std::int32_t value) -> GVariantRef<kType> {
return GVariantRef<kType>::TakeUnchecked(g_variant_new_int32(value));
}
std::int32_t Mapping<std::int32_t>::Into(const GVariantRef<kType>& variant) {
return g_variant_get_int32(variant.raw());
}
auto Mapping<std::uint32_t>::From(std::uint32_t value) -> GVariantRef<kType> {
return GVariantRef<kType>::TakeUnchecked(g_variant_new_uint32(value));
}
std::uint32_t Mapping<std::uint32_t>::Into(const GVariantRef<kType>& variant) {
return g_variant_get_uint32(variant.raw());
}
auto Mapping<std::int64_t>::From(std::int64_t value) -> GVariantRef<kType> {
return GVariantRef<kType>::TakeUnchecked(g_variant_new_int64(value));
}
std::int64_t Mapping<std::int64_t>::Into(const GVariantRef<kType>& variant) {
return g_variant_get_int64(variant.raw());
}
auto Mapping<std::uint64_t>::From(std::uint64_t value) -> GVariantRef<kType> {
return GVariantRef<kType>::TakeUnchecked(g_variant_new_uint64(value));
}
std::uint64_t Mapping<std::uint64_t>::Into(const GVariantRef<kType>& variant) {
return g_variant_get_uint64(variant.raw());
}
auto Mapping<double>::From(double value) -> GVariantRef<kType> {
return GVariantRef<kType>::TakeUnchecked(g_variant_new_double(value));
}
double Mapping<double>::Into(const GVariantRef<kType>& variant) {
return g_variant_get_double(variant.raw());
}
template <Type C>
requires(C == Type("s") || C == Type("o") || C == Type("g"))
static GVariantRef<C> CreateStringVariantUnchecked(std::string_view value) {
char* data = static_cast<char*>(g_malloc(value.size() + 1));
std::copy(value.begin(), value.end(), data);
UNSAFE_BUFFERS(data[value.size()] = '\0');
GBytes* bytes = g_bytes_new_take(data, value.size() + 1);
GVariantRef<C> variant = GVariantRef<C>::TakeUnchecked(
g_variant_new_from_bytes(C.gvariant_type(), bytes, true));
g_bytes_unref(bytes);
return variant;
}
auto Mapping<std::string>::From(const std::string& value)
-> GVariantRef<kType> {
return GVariantRef<kType>::From(std::string_view(value));
}
auto Mapping<std::string>::TryFrom(const std::string& value)
-> base::expected<GVariantRef<kType>, Loggable> {
return GVariantRef<kType>::TryFrom(std::string_view(value));
}
std::string Mapping<std::string>::Into(const GVariantRef<kType>& variant) {
gsize length;
const char* value = g_variant_get_string(variant.raw(), &length);
return std::string(value, length);
}
auto Mapping<std::string_view>::From(std::string_view value)
-> GVariantRef<kType> {
auto result = TryFrom(value);
CHECK(result.has_value()) << result.error();
return result.value();
}
auto Mapping<std::string_view>::TryFrom(std::string_view value)
-> base::expected<GVariantRef<kType>, Loggable> {
if (!g_utf8_validate(value.data(), value.length(), nullptr)) {
return base::unexpected(Loggable(FROM_HERE, "String is not valid UTF-8"));
}
return base::ok(CreateStringVariantUnchecked<kType>(value));
}
auto Mapping<const char*>::From(const char* value) -> GVariantRef<kType> {
return GVariantRef<kType>::From(std::string_view(value));
}
auto Mapping<const char*>::TryFrom(const char* value)
-> base::expected<GVariantRef<kType>, Loggable> {
return GVariantRef<kType>::TryFrom(std::string_view(value));
}
Ignored Mapping<Ignored>::Into(const GVariantRef<kType>& variant) {
return Ignored();
}
decltype(std::ignore) Mapping<decltype(std::ignore)>::Into(
const GVariantRef<kType>& variant) {
return std::ignore;
}
auto Mapping<ObjectPathCStr>::From(const ObjectPathCStr& value)
-> GVariantRef<kType> {
return CreateStringVariantUnchecked<kType>(value.c_str());
}
auto Mapping<ObjectPath>::From(const ObjectPath& value) -> GVariantRef<kType> {
return CreateStringVariantUnchecked<kType>(value.value());
}
ObjectPath Mapping<ObjectPath>::Into(const GVariantRef<kType>& variant) {
gsize length;
const char* value = g_variant_get_string(variant.raw(), &length);
return ObjectPath(std::string(value, length));
}
auto Mapping<TypeSignatureCStr>::From(const TypeSignatureCStr& value)
-> GVariantRef<kType> {
return CreateStringVariantUnchecked<kType>(value.c_str());
}
auto Mapping<TypeSignature>::From(const TypeSignature& value)
-> GVariantRef<kType> {
return CreateStringVariantUnchecked<kType>(value.value());
}
TypeSignature Mapping<TypeSignature>::Into(const GVariantRef<kType>& variant) {
gsize length;
const char* value = g_variant_get_string(variant.raw(), &length);
return TypeSignature(std::string(value, length));
}
}