blob: 7f632ec60e027ad7e1de75259af3236fd9282fe1 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#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 {
// GVariantRef implementation
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());
}
// GVariantRef implementation
// static
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);
// static
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);
// static
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);
// Wrapper implementations
ObjectPathCStr::ObjectPathCStr(const ObjectPath& path LIFETIME_BOUND)
: path_(path.c_str()) {}
// static
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()) {}
// static
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()) {}
// static
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()) {}
// static
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) {}
// Mapping implementations
// static
auto Mapping<bool>::From(bool value) -> GVariantRef<kType> {
return GVariantRef<kType>::TakeUnchecked(g_variant_new_boolean(value));
}
// static
bool Mapping<bool>::Into(const GVariantRef<kType>& variant) {
return g_variant_get_boolean(variant.raw());
}
// static
auto Mapping<std::uint8_t>::From(std::uint8_t value) -> GVariantRef<kType> {
return GVariantRef<kType>::TakeUnchecked(g_variant_new_byte(value));
}
// static
std::uint8_t Mapping<std::uint8_t>::Into(const GVariantRef<kType>& variant) {
return g_variant_get_byte(variant.raw());
}
// static
auto Mapping<std::int16_t>::From(std::int16_t value) -> GVariantRef<kType> {
return GVariantRef<kType>::TakeUnchecked(g_variant_new_int16(value));
}
// static
std::int16_t Mapping<std::int16_t>::Into(const GVariantRef<kType>& variant) {
return g_variant_get_int16(variant.raw());
}
// static
auto Mapping<std::uint16_t>::From(std::uint16_t value) -> GVariantRef<kType> {
return GVariantRef<kType>::TakeUnchecked(g_variant_new_uint16(value));
}
// static
std::uint16_t Mapping<std::uint16_t>::Into(const GVariantRef<kType>& variant) {
return g_variant_get_uint16(variant.raw());
}
// static
auto Mapping<std::int32_t>::From(std::int32_t value) -> GVariantRef<kType> {
return GVariantRef<kType>::TakeUnchecked(g_variant_new_int32(value));
}
// static
std::int32_t Mapping<std::int32_t>::Into(const GVariantRef<kType>& variant) {
return g_variant_get_int32(variant.raw());
}
// static
auto Mapping<std::uint32_t>::From(std::uint32_t value) -> GVariantRef<kType> {
return GVariantRef<kType>::TakeUnchecked(g_variant_new_uint32(value));
}
// static
std::uint32_t Mapping<std::uint32_t>::Into(const GVariantRef<kType>& variant) {
return g_variant_get_uint32(variant.raw());
}
// static
auto Mapping<std::int64_t>::From(std::int64_t value) -> GVariantRef<kType> {
return GVariantRef<kType>::TakeUnchecked(g_variant_new_int64(value));
}
// static
std::int64_t Mapping<std::int64_t>::Into(const GVariantRef<kType>& variant) {
return g_variant_get_int64(variant.raw());
}
// static
auto Mapping<std::uint64_t>::From(std::uint64_t value) -> GVariantRef<kType> {
return GVariantRef<kType>::TakeUnchecked(g_variant_new_uint64(value));
}
// static
std::uint64_t Mapping<std::uint64_t>::Into(const GVariantRef<kType>& variant) {
return g_variant_get_uint64(variant.raw());
}
// static
auto Mapping<double>::From(double value) -> GVariantRef<kType> {
return GVariantRef<kType>::TakeUnchecked(g_variant_new_double(value));
}
// static
double Mapping<double>::Into(const GVariantRef<kType>& variant) {
return g_variant_get_double(variant.raw());
}
// Creates a GVariantRef of a string-like type ("s", "o", or "g") from a
// string_view that has already been verified to be of the correct form for the
// type.
template <Type C>
requires(C == Type("s") || C == Type("o") || C == Type("g"))
static GVariantRef<C> CreateStringVariantUnchecked(std::string_view value) {
// g_variant_new_string() can't be used directly because it requires a null-
// terminated string, which |value| might not be. To avoid making two copies
// of |value| (one to add a null byte and another to construct the GVariant),
// we instead create a new backing buffer ourselves for the GVariant to use.
// The serialized form of a string GVariant is just the string contents
// followed by a null byte.
char* data = static_cast<char*>(g_malloc(value.size() + 1));
std::copy(value.begin(), value.end(), data);
// SAFETY: We allocate |data| earlier in this function to have a size of
// value.size() + 1.
UNSAFE_BUFFERS(data[value.size()] = '\0');
// GVariant holds the buffer as a reference-counted GBytes object. The GBytes
// object takes ownership of |data|, and will call g_free on it when the last
// reference is dropped.
GBytes* bytes = g_bytes_new_take(data, value.size() + 1);
// g_variant_new_from_bytes() creates a new GVariant using |bytes| as its
// backing buffer without making a copy.
GVariantRef<C> variant = GVariantRef<C>::TakeUnchecked(
g_variant_new_from_bytes(C.gvariant_type(), bytes, true));
// g_variant_new_from_bytes() takes its own ref to |bytes|.
g_bytes_unref(bytes);
return variant;
}
// static
auto Mapping<std::string>::From(const std::string& value)
-> GVariantRef<kType> {
return GVariantRef<kType>::From(std::string_view(value));
}
// static
auto Mapping<std::string>::TryFrom(const std::string& value)
-> base::expected<GVariantRef<kType>, Loggable> {
return GVariantRef<kType>::TryFrom(std::string_view(value));
}
// static
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);
}
// static
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();
}
// static
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));
}
// static
auto Mapping<const char*>::From(const char* value) -> GVariantRef<kType> {
return GVariantRef<kType>::From(std::string_view(value));
}
// static
auto Mapping<const char*>::TryFrom(const char* value)
-> base::expected<GVariantRef<kType>, Loggable> {
return GVariantRef<kType>::TryFrom(std::string_view(value));
}
// static
Ignored Mapping<Ignored>::Into(const GVariantRef<kType>& variant) {
return Ignored();
}
// static
decltype(std::ignore) Mapping<decltype(std::ignore)>::Into(
const GVariantRef<kType>& variant) {
return std::ignore;
}
// static
auto Mapping<ObjectPathCStr>::From(const ObjectPathCStr& value)
-> GVariantRef<kType> {
return CreateStringVariantUnchecked<kType>(value.c_str());
}
// static
auto Mapping<ObjectPath>::From(const ObjectPath& value) -> GVariantRef<kType> {
return CreateStringVariantUnchecked<kType>(value.value());
}
// static
ObjectPath Mapping<ObjectPath>::Into(const GVariantRef<kType>& variant) {
gsize length;
const char* value = g_variant_get_string(variant.raw(), &length);
// Value is already guaranteed to be a valid object path.
return ObjectPath(std::string(value, length));
}
// static
auto Mapping<TypeSignatureCStr>::From(const TypeSignatureCStr& value)
-> GVariantRef<kType> {
return CreateStringVariantUnchecked<kType>(value.c_str());
}
// static
auto Mapping<TypeSignature>::From(const TypeSignature& value)
-> GVariantRef<kType> {
return CreateStringVariantUnchecked<kType>(value.value());
}
// static
TypeSignature Mapping<TypeSignature>::Into(const GVariantRef<kType>& variant) {
gsize length;
const char* value = g_variant_get_string(variant.raw(), &length);
// Value is already guaranteed to be a valid object path.
return TypeSignature(std::string(value, length));
}
} // namespace remoting::gvariant