blob: fec76c93f0d5a5a361e9413f06daa1cf7cf28781 [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.
// Extensible serialization and deserialization functions for base::Pickle.
//
// This file provides a way to serialize and deserialize arbitrary types to and
// from a base::Pickle. The PickleTraits<T> class template is used to define
// serialization and deserialization for a type T.
//
// By default, all built-in integer types, bools, almost all std::, absl:: and
// base:: container types are supported, along with std::optional, tuple and
// pair types. Supported types may be nested to arbitrary depth, for example
// std::map<std::string, std::vector<int>>.
//
// To serialize a value of type T, call:
//
// base::Pickle pickle;
// net::WriteToPickle(pickle, value);
//
// To deserialize a value of type T, call:
//
// auto pickle = base::Pickle::WithData(data);
// std::optional<T> value = net::ReadValueFromPickle<T>(iter);
//
// When deserialization fails, the return value will be std::nullopt.
//
// Alternatively, when deserializing a class object, it may be more convenient
// to use ReadPickleInto(), eg.
//
// auto pickle = base::Pickle::WithData(data);
// if (!net::ReadPickleInto(pickle, instance.member1_, instance.member2_)) {
// return std::nullopt;
// }
// return instance;
//
// See pickle_traits.h for how to define serialization and deserialization for
// your own types.
//
// Limitations:
// - Trying to serialize a container with more than INT_MAX elements will
// result in a CHECK failure. base::Pickle is probably not the right tool for
// the job if you need to serialize more than 2G elements.
// - Serializing size_t will give incompatible results on 32-bit and 64-bit
// platforms. This is one reason why containers are serialized with a 32-bit
// value for the length.
//
// Intentionally unsupported:
// - pointer types
//
// Not currently supported:
// - float, double, long double
// - std::forward_list
// - std::array
// - std::variant
// - enums
#ifndef NET_BASE_PICKLE_H_
#define NET_BASE_PICKLE_H_
#include <optional>
#include <tuple>
#include "base/pickle.h"
#include "net/base/pickle_traits.h"
namespace net {
// Serializes `args` to `pickle`.
template <typename... Args>
requires(internal::CanSerialize<Args> && ...)
void WriteToPickle(base::Pickle& pickle, const Args&... args) {
static_assert((!std::is_const_v<Args> && ...));
pickle.Reserve(EstimatePickleSize(args...));
// "," is used in place of ";" here so that we can use template pack
// expansion.
(PickleTraits<Args>::Serialize(pickle, args), ...);
}
// Deserializes a single value of type T from `iter`. Returns std::nullopt on
// failure.
template <typename T>
requires(internal::CanDeserialize<T>)
std::optional<T> ReadValueFromPickle(base::PickleIterator& iter) {
// Always remove const qualifier before attempting to deserialize.
// Deserialization always creates a new value, and some deserializers will
// choke on value they can't write to.
return PickleTraits<std::remove_const_t<T>>::Deserialize(iter);
}
// Deserializes multiple values from `iter` and returns them as an optional
// tuple. Example usage:
//
// auto pickle = base::Pickle::WithData(data);
// auto maybe_value =
// net::ReadValuesFromPickle<int, std::string>(iter);
// if (!maybe_value) {
// return std::nullopt;
// }
// auto [int_param, string_param] = std::move(maybe_value).value();
// return MyType(int_param, string_param);
//
// Returns std::nullopt on failure.
template <typename... Args>
requires(internal::CanDeserialize<Args> && ...)
std::optional<std::tuple<Args...>> ReadValuesFromPickle(
base::PickleIterator& iter) {
return ReadValueFromPickle<std::tuple<Args...>>(iter);
}
// Deserializes multiple values from `iter` and stores them in `args`. Returns
// false and doesn not modify `args` on failure.
template <typename... Args>
requires(internal::CanDeserialize<Args> && ...)
[[nodiscard]] bool ReadPickleInto(base::PickleIterator& iter, Args&... args) {
auto maybe_value = ReadValuesFromPickle<Args...>(iter);
if (!maybe_value) {
return false;
}
std::tie(args...) = std::move(maybe_value).value();
return true;
}
namespace internal {
// Create a PickleIterator `iter` from `pickle` and call `f(iter, args)`. with
// it. If the input was completely consumed, return the result, otherwise
// return a value indicating failure (std::nullopt or false).
template <typename F, typename... Args>
std::invoke_result_t<F, base::PickleIterator&, Args&&...>
CallWithPickleIterator(const base::Pickle& pickle, F&& f, Args&&... args) {
base::PickleIterator iter(pickle);
auto result = std::forward<F>(f)(iter, std::forward<Args>(args)...);
if (!iter.ReachedEnd()) {
// This return statement will turn into std::nullopt or false as needed.
return {};
}
return result;
}
} // namespace internal
// Convenience version of ReadValueFromPickle that takes a base::Pickle
// instead of a base::PickleIterator. Expects the pickle to be completely
// consumed.
template <typename T>
requires(internal::CanDeserialize<T>)
std::optional<T> ReadValueFromPickle(const base::Pickle& pickle) {
// Need to specify the type to disambiguate the function pointer.
using FunctionPointerType = std::optional<T> (*)(base::PickleIterator&);
FunctionPointerType read_value_from_pickle = &ReadValueFromPickle<T>;
return internal::CallWithPickleIterator(pickle, read_value_from_pickle);
}
// Convenience version of ReadValuesFromPickle that takes a base::Pickle
// instead of a base::PickleIterator. Expects the pickle to be completely
// consumed.
template <typename... Args>
requires(internal::CanDeserialize<Args> && ...)
std::optional<std::tuple<Args...>> ReadValuesFromPickle(
const base::Pickle& pickle) {
// Need to specify the type to disambiguate the function pointer.
using FunctionPointerType =
std::optional<std::tuple<Args...>> (*)(base::PickleIterator&);
FunctionPointerType read_values_from_pickle = &ReadValuesFromPickle<Args...>;
return internal::CallWithPickleIterator(pickle, read_values_from_pickle);
}
// Convenience version of ReadPickleInto that takes a base::Pickle instead of
// a base::PickleIterator. Expects the pickle to be completely consumed.
template <typename... Args>
requires(internal::CanDeserialize<Args> && ...)
[[nodiscard]] bool ReadPickleInto(const base::Pickle& pickle, Args&... args) {
return internal::CallWithPickleIterator(pickle, &ReadPickleInto<Args...>,
args...);
}
} // namespace net
#endif // NET_BASE_PICKLE_H_