blob: 3c96f0abfebf239da99086501d9839a5edf2cee5 [file] [log] [blame]
// 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 FIELD_INSTANCE_H_
#define FIELD_INSTANCE_H_
#include <memory>
#include <string>
#include "google/protobuf/message.h"
namespace protobuf_mutator {
// Helper class for common protobuf fields operations.
class FieldInstance {
public:
static const size_t kInvalidIndex = -1;
struct Enum {
size_t index;
size_t count;
};
FieldInstance()
: message_(nullptr), descriptor_(nullptr), index_(kInvalidIndex) {}
FieldInstance(google::protobuf::Message* message,
const google::protobuf::FieldDescriptor* field, size_t index)
: message_(message), descriptor_(field), index_(index) {
assert(message_);
assert(descriptor_);
assert(index_ != kInvalidIndex);
assert(descriptor_->is_repeated());
}
FieldInstance(google::protobuf::Message* msg,
const google::protobuf::FieldDescriptor* f)
: message_(msg), descriptor_(f), index_(kInvalidIndex) {
assert(message_);
assert(descriptor_);
assert(!descriptor_->is_repeated());
}
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_);
}
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 google::protobuf::EnumValueDescriptor* value =
descriptor_->default_value_enum();
const google::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<google::protobuf::Message>* out) const {
out->reset();
}
template <class T>
void Create(const T& value) const {
if (!is_repeated()) return Store(value);
InsertRepeated(value);
}
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 google::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<google::protobuf::Message>* value) const {
const google::protobuf::Message& source =
is_repeated()
? reflection().GetRepeatedMessage(*message_, descriptor_, index_)
: reflection().GetMessage(*message_, descriptor_);
value->reset(source.New());
(*value)->CopyFrom(source);
}
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 google::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<google::protobuf::Message>& value) const {
google::protobuf::Message* mutable_message =
is_repeated()
? reflection().MutableRepeatedMessage(message_, descriptor_, index_)
: reflection().MutableMessage(message_, descriptor_);
mutable_message->Clear();
if (value) mutable_message->CopyFrom(*value);
}
google::protobuf::FieldDescriptor::CppType cpp_type() const {
return descriptor_->cpp_type();
}
const google::protobuf::EnumDescriptor* enum_type() const {
return descriptor_->enum_type();
}
const google::protobuf::Descriptor* message_type() const {
return descriptor_->message_type();
}
template <class Transformation>
void Apply(const Transformation& transformation) const {
assert(descriptor_);
using google::protobuf::FieldDescriptor;
switch (cpp_type()) {
case FieldDescriptor::CPPTYPE_INT32:
return transformation.template Apply<int32_t>(*this);
case FieldDescriptor::CPPTYPE_INT64:
return transformation.template Apply<int64_t>(*this);
case FieldDescriptor::CPPTYPE_UINT32:
return transformation.template Apply<uint32_t>(*this);
case FieldDescriptor::CPPTYPE_UINT64:
return transformation.template Apply<uint64_t>(*this);
case FieldDescriptor::CPPTYPE_DOUBLE:
return transformation.template Apply<double>(*this);
case FieldDescriptor::CPPTYPE_FLOAT:
return transformation.template Apply<float>(*this);
case FieldDescriptor::CPPTYPE_BOOL:
return transformation.template Apply<bool>(*this);
case FieldDescriptor::CPPTYPE_ENUM:
return transformation.template Apply<FieldInstance::Enum>(*this);
case FieldDescriptor::CPPTYPE_STRING:
return transformation.template Apply<std::string>(*this);
case FieldDescriptor::CPPTYPE_MESSAGE:
return transformation
.template Apply<std::unique_ptr<google::protobuf::Message>>(*this);
default:
assert(!"Unknown type");
}
}
private:
template <class T>
void InsertRepeated(const T& value) const {
PushBackRepeated(value);
int 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 (int i = field_size - 1; i > index_; --i)
reflection().SwapElements(message_, descriptor_, i, i - 1);
}
bool is_repeated() const { return descriptor_->is_repeated(); }
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 google::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<google::protobuf::Message>& value) const {
assert(is_repeated());
google::protobuf::Message* mutable_message =
reflection().AddMessage(message_, descriptor_);
mutable_message->Clear();
if (value) mutable_message->CopyFrom(*value);
}
const google::protobuf::Reflection& reflection() const {
return *message_->GetReflection();
}
google::protobuf::Message* message_;
const google::protobuf::FieldDescriptor* descriptor_;
size_t index_;
};
} // namespace protobuf_mutator
#endif // FIELD_INSTANCE_H_