910e62b5创建于 1月15日历史提交
// 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