| // Copyright 2016 Google Inc. All rights reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #ifndef SRC_FIELD_INSTANCE_H_ |
| #define SRC_FIELD_INSTANCE_H_ |
| |
| #include <memory> |
| #include <string> |
| |
| #include "port/protobuf.h" |
| |
| namespace protobuf_mutator { |
| |
| // Helper class for common protobuf fields operations. |
| class ConstFieldInstance { |
| public: |
| static const size_t kInvalidIndex = -1; |
| |
| struct Enum { |
| size_t index; |
| size_t count; |
| }; |
| |
| ConstFieldInstance() |
| : message_(nullptr), descriptor_(nullptr), index_(kInvalidIndex) {} |
| |
| ConstFieldInstance(const protobuf::Message* message, |
| const protobuf::FieldDescriptor* field, size_t index) |
| : message_(message), descriptor_(field), index_(index) { |
| assert(message_); |
| assert(descriptor_); |
| assert(index_ != kInvalidIndex); |
| assert(descriptor_->is_repeated()); |
| } |
| |
| ConstFieldInstance(const protobuf::Message* message, |
| const protobuf::FieldDescriptor* field) |
| : message_(message), descriptor_(field), index_(kInvalidIndex) { |
| assert(message_); |
| assert(descriptor_); |
| assert(!descriptor_->is_repeated()); |
| } |
| |
| void GetDefault(int32_t* out) const { |
| *out = descriptor_->default_value_int32(); |
| } |
| |
| void GetDefault(int64_t* out) const { |
| *out = descriptor_->default_value_int64(); |
| } |
| |
| void GetDefault(uint32_t* out) const { |
| *out = descriptor_->default_value_uint32(); |
| } |
| |
| void GetDefault(uint64_t* out) const { |
| *out = descriptor_->default_value_uint64(); |
| } |
| |
| void GetDefault(double* out) const { |
| *out = descriptor_->default_value_double(); |
| } |
| |
| void GetDefault(float* out) const { |
| *out = descriptor_->default_value_float(); |
| } |
| |
| void GetDefault(bool* out) const { *out = descriptor_->default_value_bool(); } |
| |
| void GetDefault(Enum* out) const { |
| const protobuf::EnumValueDescriptor* value = |
| descriptor_->default_value_enum(); |
| const protobuf::EnumDescriptor* type = value->type(); |
| *out = {static_cast<size_t>(value->index()), |
| static_cast<size_t>(type->value_count())}; |
| } |
| |
| void GetDefault(std::string* out) const { |
| *out = descriptor_->default_value_string(); |
| } |
| |
| void GetDefault(std::unique_ptr<protobuf::Message>* out) const { |
| out->reset(reflection() |
| .GetMessageFactory() |
| ->GetPrototype(descriptor_->message_type()) |
| ->New()); |
| } |
| |
| void Load(int32_t* value) const { |
| *value = is_repeated() |
| ? reflection().GetRepeatedInt32(*message_, descriptor_, index_) |
| : reflection().GetInt32(*message_, descriptor_); |
| } |
| |
| void Load(int64_t* value) const { |
| *value = is_repeated() |
| ? reflection().GetRepeatedInt64(*message_, descriptor_, index_) |
| : reflection().GetInt64(*message_, descriptor_); |
| } |
| |
| void Load(uint32_t* value) const { |
| *value = is_repeated() ? reflection().GetRepeatedUInt32(*message_, |
| descriptor_, index_) |
| : reflection().GetUInt32(*message_, descriptor_); |
| } |
| |
| void Load(uint64_t* value) const { |
| *value = is_repeated() ? reflection().GetRepeatedUInt64(*message_, |
| descriptor_, index_) |
| : reflection().GetUInt64(*message_, descriptor_); |
| } |
| |
| void Load(double* value) const { |
| *value = is_repeated() ? reflection().GetRepeatedDouble(*message_, |
| descriptor_, index_) |
| : reflection().GetDouble(*message_, descriptor_); |
| } |
| |
| void Load(float* value) const { |
| *value = is_repeated() |
| ? reflection().GetRepeatedFloat(*message_, descriptor_, index_) |
| : reflection().GetFloat(*message_, descriptor_); |
| } |
| |
| void Load(bool* value) const { |
| *value = is_repeated() |
| ? reflection().GetRepeatedBool(*message_, descriptor_, index_) |
| : reflection().GetBool(*message_, descriptor_); |
| } |
| |
| void Load(Enum* value) const { |
| const protobuf::EnumValueDescriptor* value_descriptor = |
| is_repeated() |
| ? reflection().GetRepeatedEnum(*message_, descriptor_, index_) |
| : reflection().GetEnum(*message_, descriptor_); |
| *value = {static_cast<size_t>(value_descriptor->index()), |
| static_cast<size_t>(value_descriptor->type()->value_count())}; |
| } |
| |
| void Load(std::string* value) const { |
| *value = is_repeated() ? reflection().GetRepeatedString(*message_, |
| descriptor_, index_) |
| : reflection().GetString(*message_, descriptor_); |
| } |
| |
| void Load(std::unique_ptr<protobuf::Message>* value) const { |
| const protobuf::Message& source = |
| is_repeated() |
| ? reflection().GetRepeatedMessage(*message_, descriptor_, index_) |
| : reflection().GetMessage(*message_, descriptor_); |
| value->reset(source.New()); |
| (*value)->CopyFrom(source); |
| } |
| |
| std::string name() const { return descriptor_->name(); } |
| |
| protobuf::FieldDescriptor::CppType cpp_type() const { |
| return descriptor_->cpp_type(); |
| } |
| |
| const protobuf::EnumDescriptor* enum_type() const { |
| return descriptor_->enum_type(); |
| } |
| |
| const protobuf::Descriptor* message_type() const { |
| return descriptor_->message_type(); |
| } |
| |
| bool EnforceUtf8() const { |
| return descriptor_->type() == protobuf::FieldDescriptor::TYPE_STRING && |
| descriptor()->file()->syntax() == |
| protobuf::FileDescriptor::SYNTAX_PROTO3; |
| } |
| |
| protected: |
| bool is_repeated() const { return descriptor_->is_repeated(); } |
| |
| const protobuf::Reflection& reflection() const { |
| return *message_->GetReflection(); |
| } |
| |
| const protobuf::FieldDescriptor* descriptor() const { return descriptor_; } |
| |
| size_t index() const { return index_; } |
| |
| private: |
| template <class Fn, class T> |
| friend struct FieldFunction; |
| |
| const protobuf::Message* message_; |
| const protobuf::FieldDescriptor* descriptor_; |
| size_t index_; |
| }; |
| |
| class FieldInstance : public ConstFieldInstance { |
| public: |
| static const size_t kInvalidIndex = -1; |
| |
| FieldInstance() : ConstFieldInstance(), message_(nullptr) {} |
| |
| FieldInstance(protobuf::Message* message, |
| const protobuf::FieldDescriptor* field, size_t index) |
| : ConstFieldInstance(message, field, index), message_(message) {} |
| |
| FieldInstance(protobuf::Message* message, |
| const protobuf::FieldDescriptor* field) |
| : ConstFieldInstance(message, field), message_(message) {} |
| |
| void Delete() const { |
| if (!is_repeated()) return reflection().ClearField(message_, descriptor()); |
| int field_size = reflection().FieldSize(*message_, descriptor()); |
| // API has only method to delete the last message, so we move method from |
| // the |
| // middle to the end. |
| for (int i = index() + 1; i < field_size; ++i) |
| reflection().SwapElements(message_, descriptor(), i, i - 1); |
| reflection().RemoveLast(message_, descriptor()); |
| } |
| |
| template <class T> |
| void Create(const T& value) const { |
| if (!is_repeated()) return Store(value); |
| InsertRepeated(value); |
| } |
| |
| void Store(int32_t value) const { |
| if (is_repeated()) |
| reflection().SetRepeatedInt32(message_, descriptor(), index(), value); |
| else |
| reflection().SetInt32(message_, descriptor(), value); |
| } |
| |
| void Store(int64_t value) const { |
| if (is_repeated()) |
| reflection().SetRepeatedInt64(message_, descriptor(), index(), value); |
| else |
| reflection().SetInt64(message_, descriptor(), value); |
| } |
| |
| void Store(uint32_t value) const { |
| if (is_repeated()) |
| reflection().SetRepeatedUInt32(message_, descriptor(), index(), value); |
| else |
| reflection().SetUInt32(message_, descriptor(), value); |
| } |
| |
| void Store(uint64_t value) const { |
| if (is_repeated()) |
| reflection().SetRepeatedUInt64(message_, descriptor(), index(), value); |
| else |
| reflection().SetUInt64(message_, descriptor(), value); |
| } |
| |
| void Store(double value) const { |
| if (is_repeated()) |
| reflection().SetRepeatedDouble(message_, descriptor(), index(), value); |
| else |
| reflection().SetDouble(message_, descriptor(), value); |
| } |
| |
| void Store(float value) const { |
| if (is_repeated()) |
| reflection().SetRepeatedFloat(message_, descriptor(), index(), value); |
| else |
| reflection().SetFloat(message_, descriptor(), value); |
| } |
| |
| void Store(bool value) const { |
| if (is_repeated()) |
| reflection().SetRepeatedBool(message_, descriptor(), index(), value); |
| else |
| reflection().SetBool(message_, descriptor(), value); |
| } |
| |
| void Store(const Enum& value) const { |
| assert(value.index < value.count); |
| const protobuf::EnumValueDescriptor* enum_value = |
| descriptor()->enum_type()->value(value.index); |
| if (is_repeated()) |
| reflection().SetRepeatedEnum(message_, descriptor(), index(), enum_value); |
| else |
| reflection().SetEnum(message_, descriptor(), enum_value); |
| } |
| |
| void Store(const std::string& value) const { |
| if (is_repeated()) |
| reflection().SetRepeatedString(message_, descriptor(), index(), value); |
| else |
| reflection().SetString(message_, descriptor(), value); |
| } |
| |
| void Store(const std::unique_ptr<protobuf::Message>& value) const { |
| protobuf::Message* mutable_message = |
| is_repeated() ? reflection().MutableRepeatedMessage( |
| message_, descriptor(), index()) |
| : reflection().MutableMessage(message_, descriptor()); |
| mutable_message->Clear(); |
| if (value) mutable_message->CopyFrom(*value); |
| } |
| |
| private: |
| template <class T> |
| void InsertRepeated(const T& value) const { |
| PushBackRepeated(value); |
| size_t field_size = reflection().FieldSize(*message_, descriptor()); |
| if (field_size == 1) return; |
| // API has only method to add field to the end of the list. So we add |
| // descriptor() |
| // and move it into the middle. |
| for (size_t i = field_size - 1; i > index(); --i) |
| reflection().SwapElements(message_, descriptor(), i, i - 1); |
| } |
| |
| void PushBackRepeated(int32_t value) const { |
| assert(is_repeated()); |
| reflection().AddInt32(message_, descriptor(), value); |
| } |
| |
| void PushBackRepeated(int64_t value) const { |
| assert(is_repeated()); |
| reflection().AddInt64(message_, descriptor(), value); |
| } |
| |
| void PushBackRepeated(uint32_t value) const { |
| assert(is_repeated()); |
| reflection().AddUInt32(message_, descriptor(), value); |
| } |
| |
| void PushBackRepeated(uint64_t value) const { |
| assert(is_repeated()); |
| reflection().AddUInt64(message_, descriptor(), value); |
| } |
| |
| void PushBackRepeated(double value) const { |
| assert(is_repeated()); |
| reflection().AddDouble(message_, descriptor(), value); |
| } |
| |
| void PushBackRepeated(float value) const { |
| assert(is_repeated()); |
| reflection().AddFloat(message_, descriptor(), value); |
| } |
| |
| void PushBackRepeated(bool value) const { |
| assert(is_repeated()); |
| reflection().AddBool(message_, descriptor(), value); |
| } |
| |
| void PushBackRepeated(const Enum& value) const { |
| assert(value.index < value.count); |
| const protobuf::EnumValueDescriptor* enum_value = |
| descriptor()->enum_type()->value(value.index); |
| assert(is_repeated()); |
| reflection().AddEnum(message_, descriptor(), enum_value); |
| } |
| |
| void PushBackRepeated(const std::string& value) const { |
| assert(is_repeated()); |
| reflection().AddString(message_, descriptor(), value); |
| } |
| |
| void PushBackRepeated(const std::unique_ptr<protobuf::Message>& value) const { |
| assert(is_repeated()); |
| protobuf::Message* mutable_message = |
| reflection().AddMessage(message_, descriptor()); |
| mutable_message->Clear(); |
| if (value) mutable_message->CopyFrom(*value); |
| } |
| |
| protobuf::Message* message_; |
| }; |
| |
| template <class Fn, class R = void> |
| struct FieldFunction { |
| template <class Field, class... Args> |
| R operator()(const Field& field, const Args&... args) const { |
| assert(field.descriptor()); |
| using protobuf::FieldDescriptor; |
| switch (field.cpp_type()) { |
| case FieldDescriptor::CPPTYPE_INT32: |
| return static_cast<const Fn*>(this)->template ForType<int32_t>(field, |
| args...); |
| case FieldDescriptor::CPPTYPE_INT64: |
| return static_cast<const Fn*>(this)->template ForType<int64_t>(field, |
| args...); |
| case FieldDescriptor::CPPTYPE_UINT32: |
| return static_cast<const Fn*>(this)->template ForType<uint32_t>( |
| field, args...); |
| case FieldDescriptor::CPPTYPE_UINT64: |
| return static_cast<const Fn*>(this)->template ForType<uint64_t>( |
| field, args...); |
| case FieldDescriptor::CPPTYPE_DOUBLE: |
| return static_cast<const Fn*>(this)->template ForType<double>(field, |
| args...); |
| case FieldDescriptor::CPPTYPE_FLOAT: |
| return static_cast<const Fn*>(this)->template ForType<float>(field, |
| args...); |
| case FieldDescriptor::CPPTYPE_BOOL: |
| return static_cast<const Fn*>(this)->template ForType<bool>(field, |
| args...); |
| case FieldDescriptor::CPPTYPE_ENUM: |
| return static_cast<const Fn*>(this) |
| ->template ForType<ConstFieldInstance::Enum>(field, args...); |
| case FieldDescriptor::CPPTYPE_STRING: |
| return static_cast<const Fn*>(this)->template ForType<std::string>( |
| field, args...); |
| case FieldDescriptor::CPPTYPE_MESSAGE: |
| return static_cast<const Fn*>(this) |
| ->template ForType<std::unique_ptr<protobuf::Message>>(field, |
| args...); |
| } |
| assert(false && "Unknown type"); |
| abort(); |
| } |
| }; |
| |
| } // namespace protobuf_mutator |
| |
| #endif // SRC_FIELD_INSTANCE_H_ |