Commit ceb561d6 authored by kenton@google.com's avatar kenton@google.com

Add Swap(), SwapElements(), and RemoveLast() to Reflection. Patch by Scott Stafford.

parent f22943c7
????-??-?? version 2.1.1:
C++
* Fixed bug where Message.Swap(Message) was only implemented for
optimize_for_speed. Swap now properly implemented in both modes
(Issue 91).
* Added RemoveLast and SwapElements(index1, index2) to Reflection
interface for repeated elements.
* Added Swap(Message) to Reflection interface.
2009-05-13 version 2.1.0:
General
......
......@@ -70,3 +70,5 @@ Patch contributors:
* Small patch improving performance of in Python serialization.
Alexandre Vassalotti <alexandre@peadrop.com>
* Emacs mode for Protocol Buffers (editors/protobuf-mode.el).
Scott Stafford <scott.stafford@gmail.com>
* Added Swap(), SwapElements(), and RemoveLast() to Reflection interface.
......@@ -684,13 +684,13 @@ GenerateClassMethods(io::Printer* printer) {
GenerateCopyFrom(printer);
printer->Print("\n");
GenerateSwap(printer);
printer->Print("\n");
GenerateIsInitialized(printer);
printer->Print("\n");
}
GenerateSwap(printer);
printer->Print("\n");
printer->Print(
"const ::google::protobuf::Descriptor* $classname$::GetDescriptor() const {\n"
" return descriptor();\n"
......@@ -967,22 +967,27 @@ GenerateSwap(io::Printer* printer) {
printer->Print("if (other != this) {\n");
printer->Indent();
for (int i = 0; i < descriptor_->field_count(); i++) {
const FieldDescriptor* field = descriptor_->field(i);
field_generators_.get(field).GenerateSwappingCode(printer);
}
if ( descriptor_->file()->options().optimize_for() == FileOptions::SPEED ) {
for (int i = 0; i < descriptor_->field_count(); i++) {
const FieldDescriptor* field = descriptor_->field(i);
field_generators_.get(field).GenerateSwappingCode(printer);
}
for (int i = 0; i < (descriptor_->field_count() + 31) / 32; ++i) {
printer->Print("std::swap(_has_bits_[$i$], other->_has_bits_[$i$]);\n",
"i", SimpleItoa(i));
}
for (int i = 0; i < (descriptor_->field_count() + 31) / 32; ++i) {
printer->Print("std::swap(_has_bits_[$i$], other->_has_bits_[$i$]);\n",
"i", SimpleItoa(i));
}
printer->Print("_unknown_fields_.Swap(&other->_unknown_fields_);\n");
printer->Print("std::swap(_cached_size_, other->_cached_size_);\n");
if (descriptor_->extension_range_count() > 0) {
printer->Print("_extensions_.Swap(&other->_extensions_);\n");
printer->Print("_unknown_fields_.Swap(&other->_unknown_fields_);\n");
printer->Print("std::swap(_cached_size_, other->_cached_size_);\n");
if (descriptor_->extension_range_count() > 0) {
printer->Print("_extensions_.Swap(&other->_extensions_);\n");
}
} else {
printer->Print("GetReflection()->Swap(this, other);");
}
printer->Outdent();
printer->Print("}\n");
printer->Outdent();
......
This diff is collapsed.
......@@ -497,6 +497,88 @@ Message* ExtensionSet::AddMessage(int number, FieldType type,
#undef GOOGLE_DCHECK_TYPE
void ExtensionSet::RemoveLast(int number) {
map<int, Extension>::iterator iter = extensions_.find(number);
GOOGLE_CHECK(iter != extensions_.end()) << "Index out-of-bounds (field is empty).";
Extension* extension = &iter->second;
GOOGLE_DCHECK(extension->is_repeated);
switch(cpp_type(extension->type)) {
case FieldDescriptor::CPPTYPE_INT32:
extension->repeated_int32_value->RemoveLast();
break;
case FieldDescriptor::CPPTYPE_INT64:
extension->repeated_int64_value->RemoveLast();
break;
case FieldDescriptor::CPPTYPE_UINT32:
extension->repeated_uint32_value->RemoveLast();
break;
case FieldDescriptor::CPPTYPE_UINT64:
extension->repeated_uint64_value->RemoveLast();
break;
case FieldDescriptor::CPPTYPE_FLOAT:
extension->repeated_float_value->RemoveLast();
break;
case FieldDescriptor::CPPTYPE_DOUBLE:
extension->repeated_double_value->RemoveLast();
break;
case FieldDescriptor::CPPTYPE_BOOL:
extension->repeated_bool_value->RemoveLast();
break;
case FieldDescriptor::CPPTYPE_ENUM:
extension->repeated_enum_value->RemoveLast();
break;
case FieldDescriptor::CPPTYPE_STRING:
extension->repeated_string_value->RemoveLast();
break;
case FieldDescriptor::CPPTYPE_MESSAGE:
extension->repeated_message_value->RemoveLast();
break;
}
}
void ExtensionSet::SwapElements(int number, int index1, int index2) {
map<int, Extension>::iterator iter = extensions_.find(number);
GOOGLE_CHECK(iter != extensions_.end()) << "Index out-of-bounds (field is empty).";
Extension* extension = &iter->second;
GOOGLE_DCHECK(extension->is_repeated);
switch(cpp_type(extension->type)) {
case FieldDescriptor::CPPTYPE_INT32:
extension->repeated_int32_value->SwapElements(index1, index2);
break;
case FieldDescriptor::CPPTYPE_INT64:
extension->repeated_int64_value->SwapElements(index1, index2);
break;
case FieldDescriptor::CPPTYPE_UINT32:
extension->repeated_uint32_value->SwapElements(index1, index2);
break;
case FieldDescriptor::CPPTYPE_UINT64:
extension->repeated_uint64_value->SwapElements(index1, index2);
break;
case FieldDescriptor::CPPTYPE_FLOAT:
extension->repeated_float_value->SwapElements(index1, index2);
break;
case FieldDescriptor::CPPTYPE_DOUBLE:
extension->repeated_double_value->SwapElements(index1, index2);
break;
case FieldDescriptor::CPPTYPE_BOOL:
extension->repeated_bool_value->SwapElements(index1, index2);
break;
case FieldDescriptor::CPPTYPE_ENUM:
extension->repeated_enum_value->SwapElements(index1, index2);
break;
case FieldDescriptor::CPPTYPE_STRING:
extension->repeated_string_value->SwapElements(index1, index2);
break;
case FieldDescriptor::CPPTYPE_MESSAGE:
extension->repeated_message_value->SwapElements(index1, index2);
break;
}
}
// ===================================================================
void ExtensionSet::Clear() {
......
......@@ -228,6 +228,9 @@ class LIBPROTOBUF_EXPORT ExtensionSet {
const Descriptor* message_type,
MessageFactory* factory);
void RemoveLast(int number);
void SwapElements(int number, int index1, int index2);
// -----------------------------------------------------------------
// TODO(kenton): Hardcore memory management accessors
......
......@@ -269,6 +269,61 @@ int GeneratedMessageReflection::SpaceUsed(const Message& message) const {
return total_size;
}
void GeneratedMessageReflection::Swap(
Message* message1,
Message* message2) const {
if (message1 == message2) return;
GOOGLE_CHECK_EQ(message1->GetReflection(), this)
<< "Tried to swap using reflection object incompatible with message1.";
GOOGLE_CHECK_EQ(message2->GetReflection(), this)
<< "Tried to swap using reflection object incompatible with message2.";
uint32* has_bits1 = MutableHasBits(message1);
uint32* has_bits2 = MutableHasBits(message2);
int has_bits_size = (descriptor_->field_count() + 31) / 32;
for (int i = 0; i < has_bits_size; i++) {
std::swap(has_bits1[i], has_bits2[i]);
}
for (int i = 0; i < descriptor_->field_count(); i++) {
const FieldDescriptor* field = descriptor_->field(i);
if (field->is_repeated()) {
MutableRaw<GenericRepeatedField>(message1, field)->GenericSwap(
MutableRaw<GenericRepeatedField>(message2, field));
} else {
switch (field->cpp_type()) {
#define SWAP_VALUES(CPPTYPE, TYPE) \
case FieldDescriptor::CPPTYPE_##CPPTYPE: \
swap(*MutableRaw<TYPE>(message1, field), \
*MutableRaw<TYPE>(message2, field)); \
break;
SWAP_VALUES(INT32 , int32 );
SWAP_VALUES(INT64 , int64 );
SWAP_VALUES(UINT32, uint32);
SWAP_VALUES(UINT64, uint64);
SWAP_VALUES(FLOAT , float );
SWAP_VALUES(DOUBLE, double);
SWAP_VALUES(BOOL , bool );
SWAP_VALUES(ENUM , int32 );
SWAP_VALUES(STRING, string*);
SWAP_VALUES(MESSAGE, Message*);
#undef SWAP_PRIMITIVE_VALUES
default:
GOOGLE_LOG(FATAL) << "Unimplemented type: " << field->cpp_type();
}
}
}
if (extensions_offset_ != -1) {
MutableExtensionSet(message1)->Swap(MutableExtensionSet(message2));
}
MutableUnknownFields(message1)->Swap(MutableUnknownFields(message2));
}
// -------------------------------------------------------------------
bool GeneratedMessageReflection::HasField(const Message& message,
......@@ -285,8 +340,8 @@ bool GeneratedMessageReflection::HasField(const Message& message,
int GeneratedMessageReflection::FieldSize(const Message& message,
const FieldDescriptor* field) const {
USAGE_CHECK_MESSAGE_TYPE(HasField);
USAGE_CHECK_REPEATED(HasField);
USAGE_CHECK_MESSAGE_TYPE(FieldSize);
USAGE_CHECK_REPEATED(FieldSize);
if (field->is_extension()) {
return GetExtensionSet(message).ExtensionSize(field->number());
......@@ -350,6 +405,36 @@ void GeneratedMessageReflection::ClearField(
}
}
void GeneratedMessageReflection::RemoveLast(
Message* message,
const FieldDescriptor* field) const {
USAGE_CHECK_MESSAGE_TYPE(RemoveLast);
USAGE_CHECK_REPEATED(RemoveLast);
if (field->is_extension()) {
MutableExtensionSet(message)->RemoveLast(field->number());
} else {
MutableRaw<GenericRepeatedField>(message, field)->GenericRemoveLast();
}
}
void GeneratedMessageReflection::SwapElements(
Message* message,
const FieldDescriptor* field,
int index1,
int index2) const {
USAGE_CHECK_MESSAGE_TYPE(Swap);
USAGE_CHECK_REPEATED(Swap);
if (field->is_extension()) {
MutableExtensionSet(message)->SwapElements(
field->number(), index1, index2);
} else {
MutableRaw<GenericRepeatedField>(message, field)->GenericSwapElements(
index1, index2);
}
}
namespace {
// Comparison functor for sorting FieldDescriptors by field number.
struct FieldNumberSorter {
......
......@@ -140,6 +140,10 @@ class LIBPROTOBUF_EXPORT GeneratedMessageReflection : public Reflection {
bool HasField(const Message& message, const FieldDescriptor* field) const;
int FieldSize(const Message& message, const FieldDescriptor* field) const;
void ClearField(Message* message, const FieldDescriptor* field) const;
void RemoveLast(Message* message, const FieldDescriptor* field) const;
void Swap(Message* message1, Message* message2) const;
void SwapElements(Message* message, const FieldDescriptor* field,
int index1, int index2) const;
void ListFields(const Message& message,
vector<const FieldDescriptor*>* output) const;
......
......@@ -146,6 +146,122 @@ TEST(GeneratedMessageReflectionTest, DefaultsAfterClear) {
&reflection->GetMessage(message, F("optional_import_message")));
}
TEST(GeneratedMessageReflectionTest, Swap) {
unittest::TestAllTypes message1;
unittest::TestAllTypes message2;
TestUtil::SetAllFields(&message1);
const Reflection* reflection = message1.GetReflection();
reflection->Swap(&message1, &message2);
TestUtil::ExpectClear(message1);
TestUtil::ExpectAllFieldsSet(message2);
}
TEST(GeneratedMessageReflectionTest, SwapWithBothSet) {
unittest::TestAllTypes message1;
unittest::TestAllTypes message2;
TestUtil::SetAllFields(&message1);
TestUtil::SetAllFields(&message2);
TestUtil::ModifyRepeatedFields(&message2);
const Reflection* reflection = message1.GetReflection();
reflection->Swap(&message1, &message2);
TestUtil::ExpectRepeatedFieldsModified(message1);
TestUtil::ExpectAllFieldsSet(message2);
message1.set_optional_int32(532819);
reflection->Swap(&message1, &message2);
EXPECT_EQ(532819, message2.optional_int32());
}
TEST(GeneratedMessageReflectionTest, SwapExtensions) {
unittest::TestAllExtensions message1;
unittest::TestAllExtensions message2;
TestUtil::SetAllExtensions(&message1);
const Reflection* reflection = message1.GetReflection();
reflection->Swap(&message1, &message2);
TestUtil::ExpectExtensionsClear(message1);
TestUtil::ExpectAllExtensionsSet(message2);
}
TEST(GeneratedMessageReflectionTest, SwapUnknown) {
unittest::TestEmptyMessage message1, message2;
message1.mutable_unknown_fields()->AddVarint(1234, 1);
EXPECT_EQ(1, message1.unknown_fields().field_count());
EXPECT_EQ(0, message2.unknown_fields().field_count());
const Reflection* reflection = message1.GetReflection();
reflection->Swap(&message1, &message2);
EXPECT_EQ(0, message1.unknown_fields().field_count());
EXPECT_EQ(1, message2.unknown_fields().field_count());
}
TEST(GeneratedMessageReflectionTest, RemoveLast) {
unittest::TestAllTypes message;
TestUtil::ReflectionTester reflection_tester(
unittest::TestAllTypes::descriptor());
TestUtil::SetAllFields(&message);
reflection_tester.RemoveLastRepeatedsViaReflection(&message);
TestUtil::ExpectLastRepeatedsRemoved(message);
}
TEST(GeneratedMessageReflectionTest, RemoveLastExtensions) {
unittest::TestAllExtensions message;
TestUtil::ReflectionTester reflection_tester(
unittest::TestAllExtensions::descriptor());
TestUtil::SetAllExtensions(&message);
reflection_tester.RemoveLastRepeatedsViaReflection(&message);
TestUtil::ExpectLastRepeatedExtensionsRemoved(message);
}
TEST(GeneratedMessageReflectionTest, SwapRepeatedElements) {
unittest::TestAllTypes message;
TestUtil::ReflectionTester reflection_tester(
unittest::TestAllTypes::descriptor());
TestUtil::SetAllFields(&message);
// Swap and test that fields are all swapped.
reflection_tester.SwapRepeatedsViaReflection(&message);
TestUtil::ExpectRepeatedsSwapped(message);
// Swap back and test that fields are all back to original values.
reflection_tester.SwapRepeatedsViaReflection(&message);
TestUtil::ExpectAllFieldsSet(message);
}
TEST(GeneratedMessageReflectionTest, SwapRepeatedElementsExtension) {
unittest::TestAllExtensions message;
TestUtil::ReflectionTester reflection_tester(
unittest::TestAllExtensions::descriptor());
TestUtil::SetAllExtensions(&message);
// Swap and test that fields are all swapped.
reflection_tester.SwapRepeatedsViaReflection(&message);
TestUtil::ExpectRepeatedExtensionsSwapped(message);
// Swap back and test that fields are all back to original values.
reflection_tester.SwapRepeatedsViaReflection(&message);
TestUtil::ExpectAllExtensionsSet(message);
}
TEST(GeneratedMessageReflectionTest, Extensions) {
// Set every extension to a unique value then go back and check all those
// values.
......
......@@ -97,7 +97,7 @@
// // Use the reflection interface to examine the contents.
// const Reflection* reflection = foo->GetReflection();
// assert(reflection->GetString(foo, text_field) == "Hello World!");
// assert(reflection->CountField(foo, numbers_field) == 3);
// assert(reflection->FieldSize(foo, numbers_field) == 3);
// assert(reflection->GetInt32(foo, numbers_field, 0) == 1);
// assert(reflection->GetInt32(foo, numbers_field, 1) == 5);
// assert(reflection->GetInt32(foo, numbers_field, 2) == 42);
......@@ -494,6 +494,25 @@ class LIBPROTOBUF_EXPORT Reflection {
virtual void ClearField(Message* message,
const FieldDescriptor* field) const = 0;
// Remove the last element of a repeated field.
// We don't provide a way to remove any element other than the last
// because it invites inefficient use, such as O(n^2) filtering loops
// that should have been O(n). If you want to remove an element other
// than the last, the best way to do it is to re-arrange the elements
// (using Swap()) so that the one you want removed is at the end, then
// call RemoveLast().
virtual void RemoveLast(Message* message,
const FieldDescriptor* field) const = 0;
// Swap the complete contents of two messages.
virtual void Swap(Message* message1, Message* message2) const = 0;
// Swap two elements of a repeated field.
virtual void SwapElements(Message* message,
const FieldDescriptor* field,
int index1,
int index2) const = 0;
// List all fields of the message which are currently set. This includes
// extensions. Singular fields will only be listed if HasField(field) would
// return true and repeated fields will only be listed if FieldSize(field)
......
......@@ -86,6 +86,9 @@ class LIBPROTOBUF_EXPORT GenericRepeatedField {
virtual void* GenericMutable(int index) = 0;
virtual void* GenericAdd() = 0;
virtual void GenericClear() = 0;
virtual void GenericRemoveLast() = 0;
virtual void GenericSwap(GenericRepeatedField *other) = 0;
virtual void GenericSwapElements(int index1, int index2) = 0;
virtual int GenericSize() const = 0;
virtual int GenericSpaceUsedExcludingSelf() const = 0;
......@@ -135,6 +138,9 @@ class RepeatedField : public internal::GenericRepeatedField {
// Swap entire contents with "other".
void Swap(RepeatedField* other);
// Swap two elements of a repeated field.
void SwapElements(int index1, int index2);
// STL-like iterator support
typedef Element* iterator;
typedef const Element* const_iterator;
......@@ -154,6 +160,9 @@ class RepeatedField : public internal::GenericRepeatedField {
void* GenericMutable(int index);
void* GenericAdd();
void GenericClear();
void GenericRemoveLast();
void GenericSwap(GenericRepeatedField *other);
void GenericSwapElements(int index1, int index2);
int GenericSize() const;
int GenericSpaceUsedExcludingSelf() const;
......@@ -214,6 +223,9 @@ class RepeatedPtrField : public internal::GenericRepeatedField {
// Swap entire contents with "other".
void Swap(RepeatedPtrField* other);
// Swap two elements of a repeated field.
void SwapElements(int index1, int index2);
// STL-like iterator support
typedef internal::RepeatedPtrIterator<Element**> iterator;
typedef internal::RepeatedPtrIterator<const Element* const*> const_iterator;
......@@ -266,6 +278,9 @@ class RepeatedPtrField : public internal::GenericRepeatedField {
void* GenericMutable(int index);
void* GenericAdd();
void GenericClear();
void GenericRemoveLast();
void GenericSwap(GenericRepeatedField *other);
void GenericSwapElements(int index1, int index2);
int GenericSize() const;
int GenericSpaceUsedExcludingSelf() const;
......@@ -395,6 +410,11 @@ void RepeatedField<Element>::Swap(RepeatedField* other) {
}
}
template <typename Element>
void RepeatedField<Element>::SwapElements(int index1, int index2) {
swap(*Mutable(index1), *Mutable(index2));
}
template <typename Element>
inline typename RepeatedField<Element>::iterator
RepeatedField<Element>::begin() {
......@@ -443,6 +463,21 @@ void RepeatedField<Element>::GenericClear() {
Clear();
}
template <typename Element>
void RepeatedField<Element>::GenericRemoveLast() {
RemoveLast();
}
template <typename Element>
void RepeatedField<Element>::GenericSwap(GenericRepeatedField *other) {
Swap(down_cast<RepeatedField<Element>*>(other));
}
template <typename Element>
void RepeatedField<Element>::GenericSwapElements(int index1, int index2) {
SwapElements(index1, index2);
}
template <typename Element>
int RepeatedField<Element>::GenericSize() const {
return size();
......@@ -622,6 +657,11 @@ void RepeatedPtrField<Element>::Swap(RepeatedPtrField* other) {
}
}
template <typename Element>
void RepeatedPtrField<Element>::SwapElements(int index1, int index2) {
swap(elements_[index1], elements_[index2]);
}
template <typename Element>
inline int RepeatedPtrField<Element>::SpaceUsedExcludingSelf() const {
int allocated_bytes =
......@@ -707,6 +747,21 @@ void RepeatedPtrField<Element>::GenericClear() {
Clear();
}
template <typename Element>
void RepeatedPtrField<Element>::GenericRemoveLast() {
RemoveLast();
}
template <typename Element>
void RepeatedPtrField<Element>::GenericSwap(GenericRepeatedField *other) {
Swap(down_cast<RepeatedPtrField<Element>*>(other));
}
template <typename Element>
void RepeatedPtrField<Element>::GenericSwapElements(int index1, int index2) {
SwapElements(index1, index2);
}
template <typename Element>
int RepeatedPtrField<Element>::GenericSize() const {
return size();
......@@ -736,7 +791,7 @@ inline Element* RepeatedPtrField<Element>::NewElement() {
return new Element;
}
// RepeatedPtrField<Message> is alowed but requires a prototype since Message
// RepeatedPtrField<Message> is allowed but requires a prototype since Message
// is abstract.
template <>
inline Message* RepeatedPtrField<Message>::NewElement() {
......
This diff is collapsed.
......@@ -96,6 +96,17 @@ class TestUtil {
// SetAllFieldsAndExtensions().
static void ExpectAllFieldsAndExtensionsInOrder(const string& serialized);
// Check that all repeated fields have had their last elements removed.
static void ExpectLastRepeatedsRemoved(
const unittest::TestAllTypes& message);
static void ExpectLastRepeatedExtensionsRemoved(
const unittest::TestAllExtensions& message);
// Check that all repeated fields have had their first and last elements swapped.
static void ExpectRepeatedsSwapped(const unittest::TestAllTypes& message);
static void ExpectRepeatedExtensionsSwapped(
const unittest::TestAllExtensions& message);
// Like above, but use the reflection interface.
class ReflectionTester {
public:
......@@ -116,6 +127,9 @@ class TestUtil {
void ExpectPackedFieldsSetViaReflection(const Message& message);
void ExpectPackedClearViaReflection(const Message& message);
void RemoveLastRepeatedsViaReflection(Message* message);
void SwapRepeatedsViaReflection(Message* message);
private:
const FieldDescriptor* F(const string& name);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment