280 lines
10 KiB
C++
280 lines
10 KiB
C++
// Protocol Buffers - Google's data interchange format
|
|
// Copyright 2023 Google Inc. All rights reserved.
|
|
// https://developers.google.com/protocol-buffers/
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without
|
|
// modification, are permitted provided that the following conditions are
|
|
// met:
|
|
//
|
|
// * Redistributions of source code must retain the above copyright
|
|
// notice, this list of conditions and the following disclaimer.
|
|
// * Redistributions in binary form must reproduce the above
|
|
// copyright notice, this list of conditions and the following disclaimer
|
|
// in the documentation and/or other materials provided with the
|
|
// distribution.
|
|
// * Neither the name of Google Inc. nor the names of its
|
|
// contributors may be used to endorse or promote products derived from
|
|
// this software without specific prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
#include "google/protobuf/feature_resolver.h"
|
|
|
|
#include <algorithm>
|
|
#include <iterator>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "absl/algorithm/container.h"
|
|
#include "absl/log/absl_check.h"
|
|
#include "absl/memory/memory.h"
|
|
#include "absl/status/status.h"
|
|
#include "absl/status/statusor.h"
|
|
#include "absl/strings/str_cat.h"
|
|
#include "absl/strings/str_split.h"
|
|
#include "google/protobuf/descriptor.h"
|
|
#include "google/protobuf/descriptor.pb.h"
|
|
#include "google/protobuf/dynamic_message.h"
|
|
#include "google/protobuf/message.h"
|
|
#include "google/protobuf/reflection_ops.h"
|
|
#include "google/protobuf/text_format.h"
|
|
|
|
// Must be included last.
|
|
#include "google/protobuf/port_def.inc"
|
|
|
|
#define RETURN_IF_ERROR(expr) \
|
|
do { \
|
|
const absl::Status _status = (expr); \
|
|
if (PROTOBUF_PREDICT_FALSE(!_status.ok())) return _status; \
|
|
} while (0)
|
|
|
|
namespace google {
|
|
namespace protobuf {
|
|
namespace {
|
|
|
|
template <typename... Args>
|
|
absl::Status Error(Args... args) {
|
|
return absl::FailedPreconditionError(absl::StrCat(args...));
|
|
}
|
|
|
|
bool EditionsLessThan(absl::string_view a, absl::string_view b) {
|
|
std::vector<absl::string_view> as = absl::StrSplit(a, '.');
|
|
std::vector<absl::string_view> bs = absl::StrSplit(b, '.');
|
|
size_t min_size = std::min(as.size(), bs.size());
|
|
for (size_t i = 0; i < min_size; ++i) {
|
|
if (as[i].size() != bs[i].size()) {
|
|
return as[i].size() < bs[i].size();
|
|
} else if (as[i] != bs[i]) {
|
|
return as[i] < bs[i];
|
|
}
|
|
}
|
|
// Both strings are equal up until an extra element, which makes that string
|
|
// more recent.
|
|
return as.size() < bs.size();
|
|
}
|
|
|
|
absl::Status ValidateDescriptor(absl::string_view edition,
|
|
const Descriptor& descriptor) {
|
|
if (descriptor.oneof_decl_count() > 0) {
|
|
return Error("Type ", descriptor.full_name(),
|
|
" contains unsupported oneof feature fields.");
|
|
}
|
|
for (int i = 0; i < descriptor.field_count(); ++i) {
|
|
const FieldDescriptor& field = *descriptor.field(i);
|
|
if (field.is_required()) {
|
|
return Error("Feature field ", field.full_name(),
|
|
" is an unsupported required field.");
|
|
}
|
|
if (field.is_repeated()) {
|
|
return Error("Feature field ", field.full_name(),
|
|
" is an unsupported repeated field.");
|
|
}
|
|
if (field.options().targets().empty()) {
|
|
return Error("Feature field ", field.full_name(),
|
|
" has no target specified.");
|
|
}
|
|
}
|
|
|
|
return absl::OkStatus();
|
|
}
|
|
|
|
absl::Status FillDefaults(absl::string_view edition, Message& msg) {
|
|
const Descriptor& descriptor = *msg.GetDescriptor();
|
|
|
|
auto comparator = [](const FieldOptions::EditionDefault& a,
|
|
const FieldOptions::EditionDefault& b) {
|
|
return EditionsLessThan(a.edition(), b.edition());
|
|
};
|
|
FieldOptions::EditionDefault edition_lookup;
|
|
edition_lookup.set_edition(edition);
|
|
|
|
for (int i = 0; i < descriptor.field_count(); ++i) {
|
|
const FieldDescriptor& field = *descriptor.field(i);
|
|
msg.GetReflection()->ClearField(&msg, &field);
|
|
ABSL_CHECK(!field.is_repeated());
|
|
|
|
std::vector<FieldOptions::EditionDefault> defaults{
|
|
field.options().edition_defaults().begin(),
|
|
field.options().edition_defaults().end()};
|
|
absl::c_sort(defaults, comparator);
|
|
auto first_nonmatch =
|
|
absl::c_upper_bound(defaults, edition_lookup, comparator);
|
|
if (first_nonmatch == defaults.begin()) {
|
|
return Error("No valid default found for edition ", edition,
|
|
" in feature field ", field.full_name());
|
|
}
|
|
|
|
if (field.cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
|
|
for (auto it = defaults.begin(); it != first_nonmatch; ++it) {
|
|
if (!TextFormat::MergeFromString(
|
|
it->value(),
|
|
msg.GetReflection()->MutableMessage(&msg, &field))) {
|
|
return Error("Parsing error in edition_defaults for feature field ",
|
|
field.full_name(), ". Could not parse: ", it->value());
|
|
}
|
|
}
|
|
} else {
|
|
const std::string& def = std::prev(first_nonmatch)->value();
|
|
if (!TextFormat::ParseFieldValueFromString(def, &field, &msg)) {
|
|
return Error("Parsing error in edition_defaults for feature field ",
|
|
field.full_name(), ". Could not parse: ", def);
|
|
}
|
|
}
|
|
}
|
|
|
|
return absl::OkStatus();
|
|
}
|
|
|
|
absl::Status ValidateMergedFeatures(const Message& msg) {
|
|
const Descriptor& descriptor = *msg.GetDescriptor();
|
|
const Reflection& reflection = *msg.GetReflection();
|
|
for (int i = 0; i < descriptor.field_count(); ++i) {
|
|
const FieldDescriptor& field = *descriptor.field(i);
|
|
// Validate enum features.
|
|
if (field.enum_type() != nullptr) {
|
|
ABSL_DCHECK(reflection.HasField(msg, &field));
|
|
int int_value = reflection.GetEnumValue(msg, &field);
|
|
const EnumValueDescriptor* value =
|
|
field.enum_type()->FindValueByNumber(int_value);
|
|
ABSL_DCHECK(value != nullptr);
|
|
if (value->number() == 0) {
|
|
return Error("Feature field ", field.full_name(),
|
|
" must resolve to a known value, found ", value->name());
|
|
}
|
|
}
|
|
}
|
|
|
|
return absl::OkStatus();
|
|
}
|
|
|
|
} // namespace
|
|
|
|
absl::StatusOr<FeatureResolver> FeatureResolver::Create(
|
|
absl::string_view edition, const Descriptor* descriptor) {
|
|
if (descriptor == nullptr) {
|
|
return Error(
|
|
"Unable to find definition of google.protobuf.FeatureSet in descriptor pool.");
|
|
}
|
|
|
|
RETURN_IF_ERROR(ValidateDescriptor(edition, *descriptor));
|
|
|
|
auto message_factory = absl::make_unique<DynamicMessageFactory>();
|
|
auto defaults =
|
|
absl::WrapUnique(message_factory->GetPrototype(descriptor)->New());
|
|
|
|
RETURN_IF_ERROR(FillDefaults(edition, *defaults));
|
|
|
|
return FeatureResolver(edition, *descriptor, std::move(message_factory),
|
|
std::move(defaults));
|
|
}
|
|
|
|
absl::Status FeatureResolver::RegisterExtension(
|
|
const FieldDescriptor& extension) {
|
|
if (!extension.is_extension() ||
|
|
extension.containing_type() != &descriptor_ ||
|
|
extensions_.contains(&extension)) {
|
|
// These are valid but irrelevant extensions, just return ok.
|
|
return absl::OkStatus();
|
|
}
|
|
|
|
ABSL_CHECK(descriptor_.IsExtensionNumber(extension.number()));
|
|
|
|
if (extension.message_type() == nullptr) {
|
|
return Error("FeatureSet extension ", extension.full_name(),
|
|
" is not of message type. Feature extensions should "
|
|
"always use messages to allow for evolution.");
|
|
}
|
|
|
|
if (extension.is_repeated()) {
|
|
return Error(
|
|
"Only singular features extensions are supported. Found "
|
|
"repeated extension ",
|
|
extension.full_name());
|
|
}
|
|
|
|
if (extension.message_type()->extension_count() > 0 ||
|
|
extension.message_type()->extension_range_count() > 0) {
|
|
return Error("Nested extensions in feature extension ",
|
|
extension.full_name(), " are not supported.");
|
|
}
|
|
|
|
RETURN_IF_ERROR(ValidateDescriptor(edition_, *extension.message_type()));
|
|
|
|
Message* msg =
|
|
defaults_->GetReflection()->MutableMessage(defaults_.get(), &extension);
|
|
ABSL_CHECK(msg != nullptr);
|
|
RETURN_IF_ERROR(FillDefaults(edition_, *msg));
|
|
|
|
extensions_.insert(&extension);
|
|
return absl::OkStatus();
|
|
}
|
|
|
|
absl::Status FeatureResolver::RegisterExtensions(const FileDescriptor& file) {
|
|
for (int i = 0; i < file.extension_count(); ++i) {
|
|
RETURN_IF_ERROR(RegisterExtension(*file.extension(i)));
|
|
}
|
|
for (int i = 0; i < file.message_type_count(); ++i) {
|
|
RETURN_IF_ERROR(RegisterExtensions(*file.message_type(i)));
|
|
}
|
|
return absl::OkStatus();
|
|
}
|
|
|
|
absl::Status FeatureResolver::RegisterExtensions(const Descriptor& message) {
|
|
for (int i = 0; i < message.extension_count(); ++i) {
|
|
RETURN_IF_ERROR(RegisterExtension(*message.extension(i)));
|
|
}
|
|
for (int i = 0; i < message.nested_type_count(); ++i) {
|
|
RETURN_IF_ERROR(RegisterExtensions(*message.nested_type(i)));
|
|
}
|
|
return absl::OkStatus();
|
|
}
|
|
|
|
absl::StatusOr<FeatureSet> FeatureResolver::MergeFeatures(
|
|
const FeatureSet& merged_parent, const FeatureSet& unmerged_child) const {
|
|
FeatureSet merged;
|
|
ABSL_CHECK(merged.ParseFromString(defaults_->SerializeAsString()));
|
|
merged.MergeFrom(merged_parent);
|
|
merged.MergeFrom(unmerged_child);
|
|
|
|
RETURN_IF_ERROR(ValidateMergedFeatures(merged));
|
|
|
|
return merged;
|
|
}
|
|
|
|
} // namespace protobuf
|
|
} // namespace google
|
|
|
|
#include "google/protobuf/port_undef.inc"
|