Browse Source

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

kenton@google.com 16 years ago
parent
commit
ceb561d65b

+ 10 - 0
CHANGES.txt

@@ -1,3 +1,13 @@
+????-??-?? 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

+ 2 - 0
CONTRIBUTORS.txt

@@ -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.

+ 20 - 15
src/google/protobuf/compiler/cpp/cpp_message.cc

@@ -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();

+ 167 - 167
src/google/protobuf/descriptor.pb.cc

@@ -807,6 +807,14 @@ void FileDescriptorSet::CopyFrom(const FileDescriptorSet& from) {
   MergeFrom(from);
 }
 
+bool FileDescriptorSet::IsInitialized() const {
+  
+  for (int i = 0; i < file_size(); i++) {
+    if (!this->file(i).IsInitialized()) return false;
+  }
+  return true;
+}
+
 void FileDescriptorSet::Swap(FileDescriptorSet* other) {
   if (other != this) {
     file_.Swap(&other->file_);
@@ -816,14 +824,6 @@ void FileDescriptorSet::Swap(FileDescriptorSet* other) {
   }
 }
 
-bool FileDescriptorSet::IsInitialized() const {
-  
-  for (int i = 0; i < file_size(); i++) {
-    if (!this->file(i).IsInitialized()) return false;
-  }
-  return true;
-}
-
 const ::google::protobuf::Descriptor* FileDescriptorSet::GetDescriptor() const {
   return descriptor();
 }
@@ -1274,22 +1274,6 @@ void FileDescriptorProto::CopyFrom(const FileDescriptorProto& from) {
   MergeFrom(from);
 }
 
-void FileDescriptorProto::Swap(FileDescriptorProto* other) {
-  if (other != this) {
-    std::swap(name_, other->name_);
-    std::swap(package_, other->package_);
-    dependency_.Swap(&other->dependency_);
-    message_type_.Swap(&other->message_type_);
-    enum_type_.Swap(&other->enum_type_);
-    service_.Swap(&other->service_);
-    extension_.Swap(&other->extension_);
-    std::swap(options_, other->options_);
-    std::swap(_has_bits_[0], other->_has_bits_[0]);
-    _unknown_fields_.Swap(&other->_unknown_fields_);
-    std::swap(_cached_size_, other->_cached_size_);
-  }
-}
-
 bool FileDescriptorProto::IsInitialized() const {
   
   for (int i = 0; i < message_type_size(); i++) {
@@ -1310,6 +1294,22 @@ bool FileDescriptorProto::IsInitialized() const {
   return true;
 }
 
+void FileDescriptorProto::Swap(FileDescriptorProto* other) {
+  if (other != this) {
+    std::swap(name_, other->name_);
+    std::swap(package_, other->package_);
+    dependency_.Swap(&other->dependency_);
+    message_type_.Swap(&other->message_type_);
+    enum_type_.Swap(&other->enum_type_);
+    service_.Swap(&other->service_);
+    extension_.Swap(&other->extension_);
+    std::swap(options_, other->options_);
+    std::swap(_has_bits_[0], other->_has_bits_[0]);
+    _unknown_fields_.Swap(&other->_unknown_fields_);
+    std::swap(_cached_size_, other->_cached_size_);
+  }
+}
+
 const ::google::protobuf::Descriptor* FileDescriptorProto::GetDescriptor() const {
   return descriptor();
 }
@@ -1536,6 +1536,11 @@ void DescriptorProto_ExtensionRange::CopyFrom(const DescriptorProto_ExtensionRan
   MergeFrom(from);
 }
 
+bool DescriptorProto_ExtensionRange::IsInitialized() const {
+  
+  return true;
+}
+
 void DescriptorProto_ExtensionRange::Swap(DescriptorProto_ExtensionRange* other) {
   if (other != this) {
     std::swap(start_, other->start_);
@@ -1546,11 +1551,6 @@ void DescriptorProto_ExtensionRange::Swap(DescriptorProto_ExtensionRange* other)
   }
 }
 
-bool DescriptorProto_ExtensionRange::IsInitialized() const {
-  
-  return true;
-}
-
 const ::google::protobuf::Descriptor* DescriptorProto_ExtensionRange::GetDescriptor() const {
   return descriptor();
 }
@@ -1960,21 +1960,6 @@ void DescriptorProto::CopyFrom(const DescriptorProto& from) {
   MergeFrom(from);
 }
 
-void DescriptorProto::Swap(DescriptorProto* other) {
-  if (other != this) {
-    std::swap(name_, other->name_);
-    field_.Swap(&other->field_);
-    extension_.Swap(&other->extension_);
-    nested_type_.Swap(&other->nested_type_);
-    enum_type_.Swap(&other->enum_type_);
-    extension_range_.Swap(&other->extension_range_);
-    std::swap(options_, other->options_);
-    std::swap(_has_bits_[0], other->_has_bits_[0]);
-    _unknown_fields_.Swap(&other->_unknown_fields_);
-    std::swap(_cached_size_, other->_cached_size_);
-  }
-}
-
 bool DescriptorProto::IsInitialized() const {
   
   for (int i = 0; i < field_size(); i++) {
@@ -1995,6 +1980,21 @@ bool DescriptorProto::IsInitialized() const {
   return true;
 }
 
+void DescriptorProto::Swap(DescriptorProto* other) {
+  if (other != this) {
+    std::swap(name_, other->name_);
+    field_.Swap(&other->field_);
+    extension_.Swap(&other->extension_);
+    nested_type_.Swap(&other->nested_type_);
+    enum_type_.Swap(&other->enum_type_);
+    extension_range_.Swap(&other->extension_range_);
+    std::swap(options_, other->options_);
+    std::swap(_has_bits_[0], other->_has_bits_[0]);
+    _unknown_fields_.Swap(&other->_unknown_fields_);
+    std::swap(_cached_size_, other->_cached_size_);
+  }
+}
+
 const ::google::protobuf::Descriptor* DescriptorProto::GetDescriptor() const {
   return descriptor();
 }
@@ -2546,6 +2546,14 @@ void FieldDescriptorProto::CopyFrom(const FieldDescriptorProto& from) {
   MergeFrom(from);
 }
 
+bool FieldDescriptorProto::IsInitialized() const {
+  
+  if (has_options()) {
+    if (!this->options().IsInitialized()) return false;
+  }
+  return true;
+}
+
 void FieldDescriptorProto::Swap(FieldDescriptorProto* other) {
   if (other != this) {
     std::swap(name_, other->name_);
@@ -2562,14 +2570,6 @@ void FieldDescriptorProto::Swap(FieldDescriptorProto* other) {
   }
 }
 
-bool FieldDescriptorProto::IsInitialized() const {
-  
-  if (has_options()) {
-    if (!this->options().IsInitialized()) return false;
-  }
-  return true;
-}
-
 const ::google::protobuf::Descriptor* FieldDescriptorProto::GetDescriptor() const {
   return descriptor();
 }
@@ -2839,17 +2839,6 @@ void EnumDescriptorProto::CopyFrom(const EnumDescriptorProto& from) {
   MergeFrom(from);
 }
 
-void EnumDescriptorProto::Swap(EnumDescriptorProto* other) {
-  if (other != this) {
-    std::swap(name_, other->name_);
-    value_.Swap(&other->value_);
-    std::swap(options_, other->options_);
-    std::swap(_has_bits_[0], other->_has_bits_[0]);
-    _unknown_fields_.Swap(&other->_unknown_fields_);
-    std::swap(_cached_size_, other->_cached_size_);
-  }
-}
-
 bool EnumDescriptorProto::IsInitialized() const {
   
   for (int i = 0; i < value_size(); i++) {
@@ -2861,6 +2850,17 @@ bool EnumDescriptorProto::IsInitialized() const {
   return true;
 }
 
+void EnumDescriptorProto::Swap(EnumDescriptorProto* other) {
+  if (other != this) {
+    std::swap(name_, other->name_);
+    value_.Swap(&other->value_);
+    std::swap(options_, other->options_);
+    std::swap(_has_bits_[0], other->_has_bits_[0]);
+    _unknown_fields_.Swap(&other->_unknown_fields_);
+    std::swap(_cached_size_, other->_cached_size_);
+  }
+}
+
 const ::google::protobuf::Descriptor* EnumDescriptorProto::GetDescriptor() const {
   return descriptor();
 }
@@ -3132,6 +3132,14 @@ void EnumValueDescriptorProto::CopyFrom(const EnumValueDescriptorProto& from) {
   MergeFrom(from);
 }
 
+bool EnumValueDescriptorProto::IsInitialized() const {
+  
+  if (has_options()) {
+    if (!this->options().IsInitialized()) return false;
+  }
+  return true;
+}
+
 void EnumValueDescriptorProto::Swap(EnumValueDescriptorProto* other) {
   if (other != this) {
     std::swap(name_, other->name_);
@@ -3143,14 +3151,6 @@ void EnumValueDescriptorProto::Swap(EnumValueDescriptorProto* other) {
   }
 }
 
-bool EnumValueDescriptorProto::IsInitialized() const {
-  
-  if (has_options()) {
-    if (!this->options().IsInitialized()) return false;
-  }
-  return true;
-}
-
 const ::google::protobuf::Descriptor* EnumValueDescriptorProto::GetDescriptor() const {
   return descriptor();
 }
@@ -3420,17 +3420,6 @@ void ServiceDescriptorProto::CopyFrom(const ServiceDescriptorProto& from) {
   MergeFrom(from);
 }
 
-void ServiceDescriptorProto::Swap(ServiceDescriptorProto* other) {
-  if (other != this) {
-    std::swap(name_, other->name_);
-    method_.Swap(&other->method_);
-    std::swap(options_, other->options_);
-    std::swap(_has_bits_[0], other->_has_bits_[0]);
-    _unknown_fields_.Swap(&other->_unknown_fields_);
-    std::swap(_cached_size_, other->_cached_size_);
-  }
-}
-
 bool ServiceDescriptorProto::IsInitialized() const {
   
   for (int i = 0; i < method_size(); i++) {
@@ -3442,6 +3431,17 @@ bool ServiceDescriptorProto::IsInitialized() const {
   return true;
 }
 
+void ServiceDescriptorProto::Swap(ServiceDescriptorProto* other) {
+  if (other != this) {
+    std::swap(name_, other->name_);
+    method_.Swap(&other->method_);
+    std::swap(options_, other->options_);
+    std::swap(_has_bits_[0], other->_has_bits_[0]);
+    _unknown_fields_.Swap(&other->_unknown_fields_);
+    std::swap(_cached_size_, other->_cached_size_);
+  }
+}
+
 const ::google::protobuf::Descriptor* ServiceDescriptorProto::GetDescriptor() const {
   return descriptor();
 }
@@ -3760,6 +3760,14 @@ void MethodDescriptorProto::CopyFrom(const MethodDescriptorProto& from) {
   MergeFrom(from);
 }
 
+bool MethodDescriptorProto::IsInitialized() const {
+  
+  if (has_options()) {
+    if (!this->options().IsInitialized()) return false;
+  }
+  return true;
+}
+
 void MethodDescriptorProto::Swap(MethodDescriptorProto* other) {
   if (other != this) {
     std::swap(name_, other->name_);
@@ -3772,14 +3780,6 @@ void MethodDescriptorProto::Swap(MethodDescriptorProto* other) {
   }
 }
 
-bool MethodDescriptorProto::IsInitialized() const {
-  
-  if (has_options()) {
-    if (!this->options().IsInitialized()) return false;
-  }
-  return true;
-}
-
 const ::google::protobuf::Descriptor* MethodDescriptorProto::GetDescriptor() const {
   return descriptor();
 }
@@ -4163,6 +4163,15 @@ void FileOptions::CopyFrom(const FileOptions& from) {
   MergeFrom(from);
 }
 
+bool FileOptions::IsInitialized() const {
+  
+  for (int i = 0; i < uninterpreted_option_size(); i++) {
+    if (!this->uninterpreted_option(i).IsInitialized()) return false;
+  }
+  
+  if (!_extensions_.IsInitialized()) return false;  return true;
+}
+
 void FileOptions::Swap(FileOptions* other) {
   if (other != this) {
     std::swap(java_package_, other->java_package_);
@@ -4177,15 +4186,6 @@ void FileOptions::Swap(FileOptions* other) {
   }
 }
 
-bool FileOptions::IsInitialized() const {
-  
-  for (int i = 0; i < uninterpreted_option_size(); i++) {
-    if (!this->uninterpreted_option(i).IsInitialized()) return false;
-  }
-  
-  if (!_extensions_.IsInitialized()) return false;  return true;
-}
-
 const ::google::protobuf::Descriptor* FileOptions::GetDescriptor() const {
   return descriptor();
 }
@@ -4425,6 +4425,15 @@ void MessageOptions::CopyFrom(const MessageOptions& from) {
   MergeFrom(from);
 }
 
+bool MessageOptions::IsInitialized() const {
+  
+  for (int i = 0; i < uninterpreted_option_size(); i++) {
+    if (!this->uninterpreted_option(i).IsInitialized()) return false;
+  }
+  
+  if (!_extensions_.IsInitialized()) return false;  return true;
+}
+
 void MessageOptions::Swap(MessageOptions* other) {
   if (other != this) {
     std::swap(message_set_wire_format_, other->message_set_wire_format_);
@@ -4436,15 +4445,6 @@ void MessageOptions::Swap(MessageOptions* other) {
   }
 }
 
-bool MessageOptions::IsInitialized() const {
-  
-  for (int i = 0; i < uninterpreted_option_size(); i++) {
-    if (!this->uninterpreted_option(i).IsInitialized()) return false;
-  }
-  
-  if (!_extensions_.IsInitialized()) return false;  return true;
-}
-
 const ::google::protobuf::Descriptor* MessageOptions::GetDescriptor() const {
   return descriptor();
 }
@@ -4821,6 +4821,15 @@ void FieldOptions::CopyFrom(const FieldOptions& from) {
   MergeFrom(from);
 }
 
+bool FieldOptions::IsInitialized() const {
+  
+  for (int i = 0; i < uninterpreted_option_size(); i++) {
+    if (!this->uninterpreted_option(i).IsInitialized()) return false;
+  }
+  
+  if (!_extensions_.IsInitialized()) return false;  return true;
+}
+
 void FieldOptions::Swap(FieldOptions* other) {
   if (other != this) {
     std::swap(ctype_, other->ctype_);
@@ -4835,15 +4844,6 @@ void FieldOptions::Swap(FieldOptions* other) {
   }
 }
 
-bool FieldOptions::IsInitialized() const {
-  
-  for (int i = 0; i < uninterpreted_option_size(); i++) {
-    if (!this->uninterpreted_option(i).IsInitialized()) return false;
-  }
-  
-  if (!_extensions_.IsInitialized()) return false;  return true;
-}
-
 const ::google::protobuf::Descriptor* FieldOptions::GetDescriptor() const {
   return descriptor();
 }
@@ -5043,6 +5043,15 @@ void EnumOptions::CopyFrom(const EnumOptions& from) {
   MergeFrom(from);
 }
 
+bool EnumOptions::IsInitialized() const {
+  
+  for (int i = 0; i < uninterpreted_option_size(); i++) {
+    if (!this->uninterpreted_option(i).IsInitialized()) return false;
+  }
+  
+  if (!_extensions_.IsInitialized()) return false;  return true;
+}
+
 void EnumOptions::Swap(EnumOptions* other) {
   if (other != this) {
     uninterpreted_option_.Swap(&other->uninterpreted_option_);
@@ -5053,15 +5062,6 @@ void EnumOptions::Swap(EnumOptions* other) {
   }
 }
 
-bool EnumOptions::IsInitialized() const {
-  
-  for (int i = 0; i < uninterpreted_option_size(); i++) {
-    if (!this->uninterpreted_option(i).IsInitialized()) return false;
-  }
-  
-  if (!_extensions_.IsInitialized()) return false;  return true;
-}
-
 const ::google::protobuf::Descriptor* EnumOptions::GetDescriptor() const {
   return descriptor();
 }
@@ -5261,6 +5261,15 @@ void EnumValueOptions::CopyFrom(const EnumValueOptions& from) {
   MergeFrom(from);
 }
 
+bool EnumValueOptions::IsInitialized() const {
+  
+  for (int i = 0; i < uninterpreted_option_size(); i++) {
+    if (!this->uninterpreted_option(i).IsInitialized()) return false;
+  }
+  
+  if (!_extensions_.IsInitialized()) return false;  return true;
+}
+
 void EnumValueOptions::Swap(EnumValueOptions* other) {
   if (other != this) {
     uninterpreted_option_.Swap(&other->uninterpreted_option_);
@@ -5271,15 +5280,6 @@ void EnumValueOptions::Swap(EnumValueOptions* other) {
   }
 }
 
-bool EnumValueOptions::IsInitialized() const {
-  
-  for (int i = 0; i < uninterpreted_option_size(); i++) {
-    if (!this->uninterpreted_option(i).IsInitialized()) return false;
-  }
-  
-  if (!_extensions_.IsInitialized()) return false;  return true;
-}
-
 const ::google::protobuf::Descriptor* EnumValueOptions::GetDescriptor() const {
   return descriptor();
 }
@@ -5479,6 +5479,15 @@ void ServiceOptions::CopyFrom(const ServiceOptions& from) {
   MergeFrom(from);
 }
 
+bool ServiceOptions::IsInitialized() const {
+  
+  for (int i = 0; i < uninterpreted_option_size(); i++) {
+    if (!this->uninterpreted_option(i).IsInitialized()) return false;
+  }
+  
+  if (!_extensions_.IsInitialized()) return false;  return true;
+}
+
 void ServiceOptions::Swap(ServiceOptions* other) {
   if (other != this) {
     uninterpreted_option_.Swap(&other->uninterpreted_option_);
@@ -5489,15 +5498,6 @@ void ServiceOptions::Swap(ServiceOptions* other) {
   }
 }
 
-bool ServiceOptions::IsInitialized() const {
-  
-  for (int i = 0; i < uninterpreted_option_size(); i++) {
-    if (!this->uninterpreted_option(i).IsInitialized()) return false;
-  }
-  
-  if (!_extensions_.IsInitialized()) return false;  return true;
-}
-
 const ::google::protobuf::Descriptor* ServiceOptions::GetDescriptor() const {
   return descriptor();
 }
@@ -5697,6 +5697,15 @@ void MethodOptions::CopyFrom(const MethodOptions& from) {
   MergeFrom(from);
 }
 
+bool MethodOptions::IsInitialized() const {
+  
+  for (int i = 0; i < uninterpreted_option_size(); i++) {
+    if (!this->uninterpreted_option(i).IsInitialized()) return false;
+  }
+  
+  if (!_extensions_.IsInitialized()) return false;  return true;
+}
+
 void MethodOptions::Swap(MethodOptions* other) {
   if (other != this) {
     uninterpreted_option_.Swap(&other->uninterpreted_option_);
@@ -5707,15 +5716,6 @@ void MethodOptions::Swap(MethodOptions* other) {
   }
 }
 
-bool MethodOptions::IsInitialized() const {
-  
-  for (int i = 0; i < uninterpreted_option_size(); i++) {
-    if (!this->uninterpreted_option(i).IsInitialized()) return false;
-  }
-  
-  if (!_extensions_.IsInitialized()) return false;  return true;
-}
-
 const ::google::protobuf::Descriptor* MethodOptions::GetDescriptor() const {
   return descriptor();
 }
@@ -5945,6 +5945,12 @@ void UninterpretedOption_NamePart::CopyFrom(const UninterpretedOption_NamePart&
   MergeFrom(from);
 }
 
+bool UninterpretedOption_NamePart::IsInitialized() const {
+  if ((_has_bits_[0] & 0x00000003) != 0x00000003) return false;
+  
+  return true;
+}
+
 void UninterpretedOption_NamePart::Swap(UninterpretedOption_NamePart* other) {
   if (other != this) {
     std::swap(name_part_, other->name_part_);
@@ -5955,12 +5961,6 @@ void UninterpretedOption_NamePart::Swap(UninterpretedOption_NamePart* other) {
   }
 }
 
-bool UninterpretedOption_NamePart::IsInitialized() const {
-  if ((_has_bits_[0] & 0x00000003) != 0x00000003) return false;
-  
-  return true;
-}
-
 const ::google::protobuf::Descriptor* UninterpretedOption_NamePart::GetDescriptor() const {
   return descriptor();
 }
@@ -6342,6 +6342,14 @@ void UninterpretedOption::CopyFrom(const UninterpretedOption& from) {
   MergeFrom(from);
 }
 
+bool UninterpretedOption::IsInitialized() const {
+  
+  for (int i = 0; i < name_size(); i++) {
+    if (!this->name(i).IsInitialized()) return false;
+  }
+  return true;
+}
+
 void UninterpretedOption::Swap(UninterpretedOption* other) {
   if (other != this) {
     name_.Swap(&other->name_);
@@ -6356,14 +6364,6 @@ void UninterpretedOption::Swap(UninterpretedOption* other) {
   }
 }
 
-bool UninterpretedOption::IsInitialized() const {
-  
-  for (int i = 0; i < name_size(); i++) {
-    if (!this->name(i).IsInitialized()) return false;
-  }
-  return true;
-}
-
 const ::google::protobuf::Descriptor* UninterpretedOption::GetDescriptor() const {
   return descriptor();
 }

+ 82 - 0
src/google/protobuf/extension_set.cc

@@ -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() {

+ 3 - 0
src/google/protobuf/extension_set.h

@@ -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
 

+ 87 - 2
src/google/protobuf/generated_message_reflection.cc

@@ -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 {

+ 4 - 0
src/google/protobuf/generated_message_reflection.h

@@ -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;
 

+ 116 - 0
src/google/protobuf/generated_message_reflection_unittest.cc

@@ -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.

+ 20 - 1
src/google/protobuf/message.h

@@ -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)

+ 56 - 1
src/google/protobuf/repeated_field.h

@@ -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() {

+ 315 - 0
src/google/protobuf/test_util.cc

@@ -1644,6 +1644,295 @@ void TestUtil::ExpectAllFieldsAndExtensionsInOrder(const string& serialized) {
   EXPECT_TRUE(serialized == expected);
 }
 
+void TestUtil::ExpectLastRepeatedsRemoved(
+    const unittest::TestAllTypes& message) {
+  ASSERT_EQ(1, message.repeated_int32_size   ());
+  ASSERT_EQ(1, message.repeated_int64_size   ());
+  ASSERT_EQ(1, message.repeated_uint32_size  ());
+  ASSERT_EQ(1, message.repeated_uint64_size  ());
+  ASSERT_EQ(1, message.repeated_sint32_size  ());
+  ASSERT_EQ(1, message.repeated_sint64_size  ());
+  ASSERT_EQ(1, message.repeated_fixed32_size ());
+  ASSERT_EQ(1, message.repeated_fixed64_size ());
+  ASSERT_EQ(1, message.repeated_sfixed32_size());
+  ASSERT_EQ(1, message.repeated_sfixed64_size());
+  ASSERT_EQ(1, message.repeated_float_size   ());
+  ASSERT_EQ(1, message.repeated_double_size  ());
+  ASSERT_EQ(1, message.repeated_bool_size    ());
+  ASSERT_EQ(1, message.repeated_string_size  ());
+  ASSERT_EQ(1, message.repeated_bytes_size   ());
+
+  ASSERT_EQ(1, message.repeatedgroup_size           ());
+  ASSERT_EQ(1, message.repeated_nested_message_size ());
+  ASSERT_EQ(1, message.repeated_foreign_message_size());
+  ASSERT_EQ(1, message.repeated_import_message_size ());
+  ASSERT_EQ(1, message.repeated_nested_enum_size    ());
+  ASSERT_EQ(1, message.repeated_foreign_enum_size   ());
+  ASSERT_EQ(1, message.repeated_import_enum_size    ());
+
+#ifndef PROTOBUF_TEST_NO_DESCRIPTORS
+  ASSERT_EQ(1, message.repeated_string_piece_size());
+  ASSERT_EQ(1, message.repeated_cord_size());
+#endif
+
+  // Test that the remaining element is the correct one.
+  EXPECT_EQ(201  , message.repeated_int32   (0));
+  EXPECT_EQ(202  , message.repeated_int64   (0));
+  EXPECT_EQ(203  , message.repeated_uint32  (0));
+  EXPECT_EQ(204  , message.repeated_uint64  (0));
+  EXPECT_EQ(205  , message.repeated_sint32  (0));
+  EXPECT_EQ(206  , message.repeated_sint64  (0));
+  EXPECT_EQ(207  , message.repeated_fixed32 (0));
+  EXPECT_EQ(208  , message.repeated_fixed64 (0));
+  EXPECT_EQ(209  , message.repeated_sfixed32(0));
+  EXPECT_EQ(210  , message.repeated_sfixed64(0));
+  EXPECT_EQ(211  , message.repeated_float   (0));
+  EXPECT_EQ(212  , message.repeated_double  (0));
+  EXPECT_EQ(true , message.repeated_bool    (0));
+  EXPECT_EQ("215", message.repeated_string  (0));
+  EXPECT_EQ("216", message.repeated_bytes   (0));
+
+  EXPECT_EQ(217, message.repeatedgroup           (0).a());
+  EXPECT_EQ(218, message.repeated_nested_message (0).bb());
+  EXPECT_EQ(219, message.repeated_foreign_message(0).c());
+  EXPECT_EQ(220, message.repeated_import_message (0).d());
+
+  EXPECT_EQ(unittest::TestAllTypes::BAR, message.repeated_nested_enum (0));
+  EXPECT_EQ(unittest::FOREIGN_BAR      , message.repeated_foreign_enum(0));
+  EXPECT_EQ(unittest_import::IMPORT_BAR, message.repeated_import_enum (0));
+}
+
+void TestUtil::ExpectLastRepeatedExtensionsRemoved(
+    const unittest::TestAllExtensions& message) {
+
+  // Test that one element was removed.
+  ASSERT_EQ(1, message.ExtensionSize(unittest::repeated_int32_extension   ));
+  ASSERT_EQ(1, message.ExtensionSize(unittest::repeated_int64_extension   ));
+  ASSERT_EQ(1, message.ExtensionSize(unittest::repeated_uint32_extension  ));
+  ASSERT_EQ(1, message.ExtensionSize(unittest::repeated_uint64_extension  ));
+  ASSERT_EQ(1, message.ExtensionSize(unittest::repeated_sint32_extension  ));
+  ASSERT_EQ(1, message.ExtensionSize(unittest::repeated_sint64_extension  ));
+  ASSERT_EQ(1, message.ExtensionSize(unittest::repeated_fixed32_extension ));
+  ASSERT_EQ(1, message.ExtensionSize(unittest::repeated_fixed64_extension ));
+  ASSERT_EQ(1, message.ExtensionSize(unittest::repeated_sfixed32_extension));
+  ASSERT_EQ(1, message.ExtensionSize(unittest::repeated_sfixed64_extension));
+  ASSERT_EQ(1, message.ExtensionSize(unittest::repeated_float_extension   ));
+  ASSERT_EQ(1, message.ExtensionSize(unittest::repeated_double_extension  ));
+  ASSERT_EQ(1, message.ExtensionSize(unittest::repeated_bool_extension    ));
+  ASSERT_EQ(1, message.ExtensionSize(unittest::repeated_string_extension  ));
+  ASSERT_EQ(1, message.ExtensionSize(unittest::repeated_bytes_extension   ));
+
+  ASSERT_EQ(1, message.ExtensionSize(unittest::repeatedgroup_extension           ));
+  ASSERT_EQ(1, message.ExtensionSize(unittest::repeated_nested_message_extension ));
+  ASSERT_EQ(1, message.ExtensionSize(unittest::repeated_foreign_message_extension));
+  ASSERT_EQ(1, message.ExtensionSize(unittest::repeated_import_message_extension ));
+  ASSERT_EQ(1, message.ExtensionSize(unittest::repeated_nested_enum_extension    ));
+  ASSERT_EQ(1, message.ExtensionSize(unittest::repeated_foreign_enum_extension   ));
+  ASSERT_EQ(1, message.ExtensionSize(unittest::repeated_import_enum_extension    ));
+
+  ASSERT_EQ(1, message.ExtensionSize(unittest::repeated_string_piece_extension));
+  ASSERT_EQ(1, message.ExtensionSize(unittest::repeated_cord_extension));
+
+  // Test that the remaining element is the correct one.
+  EXPECT_EQ(201  , message.GetExtension(unittest::repeated_int32_extension   , 0));
+  EXPECT_EQ(202  , message.GetExtension(unittest::repeated_int64_extension   , 0));
+  EXPECT_EQ(203  , message.GetExtension(unittest::repeated_uint32_extension  , 0));
+  EXPECT_EQ(204  , message.GetExtension(unittest::repeated_uint64_extension  , 0));
+  EXPECT_EQ(205  , message.GetExtension(unittest::repeated_sint32_extension  , 0));
+  EXPECT_EQ(206  , message.GetExtension(unittest::repeated_sint64_extension  , 0));
+  EXPECT_EQ(207  , message.GetExtension(unittest::repeated_fixed32_extension , 0));
+  EXPECT_EQ(208  , message.GetExtension(unittest::repeated_fixed64_extension , 0));
+  EXPECT_EQ(209  , message.GetExtension(unittest::repeated_sfixed32_extension, 0));
+  EXPECT_EQ(210  , message.GetExtension(unittest::repeated_sfixed64_extension, 0));
+  EXPECT_EQ(211  , message.GetExtension(unittest::repeated_float_extension   , 0));
+  EXPECT_EQ(212  , message.GetExtension(unittest::repeated_double_extension  , 0));
+  EXPECT_EQ(true , message.GetExtension(unittest::repeated_bool_extension    , 0));
+  EXPECT_EQ("215", message.GetExtension(unittest::repeated_string_extension  , 0));
+  EXPECT_EQ("216", message.GetExtension(unittest::repeated_bytes_extension   , 0));
+
+  EXPECT_EQ(217, message.GetExtension(unittest::repeatedgroup_extension           , 0).a());
+  EXPECT_EQ(218, message.GetExtension(unittest::repeated_nested_message_extension , 0).bb());
+  EXPECT_EQ(219, message.GetExtension(unittest::repeated_foreign_message_extension, 0).c());
+  EXPECT_EQ(220, message.GetExtension(unittest::repeated_import_message_extension , 0).d());
+
+  EXPECT_EQ(unittest::TestAllTypes::BAR, message.GetExtension(unittest::repeated_nested_enum_extension , 0));
+  EXPECT_EQ(unittest::FOREIGN_BAR      , message.GetExtension(unittest::repeated_foreign_enum_extension, 0));
+  EXPECT_EQ(unittest_import::IMPORT_BAR, message.GetExtension(unittest::repeated_import_enum_extension , 0));
+
+  EXPECT_EQ("224", message.GetExtension(unittest::repeated_string_piece_extension, 0));
+  EXPECT_EQ("225", message.GetExtension(unittest::repeated_cord_extension, 0));
+}
+
+void TestUtil::ExpectRepeatedsSwapped(
+    const unittest::TestAllTypes& message) {
+  ASSERT_EQ(2, message.repeated_int32_size   ());
+  ASSERT_EQ(2, message.repeated_int64_size   ());
+  ASSERT_EQ(2, message.repeated_uint32_size  ());
+  ASSERT_EQ(2, message.repeated_uint64_size  ());
+  ASSERT_EQ(2, message.repeated_sint32_size  ());
+  ASSERT_EQ(2, message.repeated_sint64_size  ());
+  ASSERT_EQ(2, message.repeated_fixed32_size ());
+  ASSERT_EQ(2, message.repeated_fixed64_size ());
+  ASSERT_EQ(2, message.repeated_sfixed32_size());
+  ASSERT_EQ(2, message.repeated_sfixed64_size());
+  ASSERT_EQ(2, message.repeated_float_size   ());
+  ASSERT_EQ(2, message.repeated_double_size  ());
+  ASSERT_EQ(2, message.repeated_bool_size    ());
+  ASSERT_EQ(2, message.repeated_string_size  ());
+  ASSERT_EQ(2, message.repeated_bytes_size   ());
+
+  ASSERT_EQ(2, message.repeatedgroup_size           ());
+  ASSERT_EQ(2, message.repeated_nested_message_size ());
+  ASSERT_EQ(2, message.repeated_foreign_message_size());
+  ASSERT_EQ(2, message.repeated_import_message_size ());
+  ASSERT_EQ(2, message.repeated_nested_enum_size    ());
+  ASSERT_EQ(2, message.repeated_foreign_enum_size   ());
+  ASSERT_EQ(2, message.repeated_import_enum_size    ());
+
+#ifndef PROTOBUF_TEST_NO_DESCRIPTORS
+  ASSERT_EQ(2, message.repeated_string_piece_size());
+  ASSERT_EQ(2, message.repeated_cord_size());
+#endif
+
+  // Test that the first element and second element are flipped.
+  EXPECT_EQ(201  , message.repeated_int32   (1));
+  EXPECT_EQ(202  , message.repeated_int64   (1));
+  EXPECT_EQ(203  , message.repeated_uint32  (1));
+  EXPECT_EQ(204  , message.repeated_uint64  (1));
+  EXPECT_EQ(205  , message.repeated_sint32  (1));
+  EXPECT_EQ(206  , message.repeated_sint64  (1));
+  EXPECT_EQ(207  , message.repeated_fixed32 (1));
+  EXPECT_EQ(208  , message.repeated_fixed64 (1));
+  EXPECT_EQ(209  , message.repeated_sfixed32(1));
+  EXPECT_EQ(210  , message.repeated_sfixed64(1));
+  EXPECT_EQ(211  , message.repeated_float   (1));
+  EXPECT_EQ(212  , message.repeated_double  (1));
+  EXPECT_EQ(true , message.repeated_bool    (1));
+  EXPECT_EQ("215", message.repeated_string  (1));
+  EXPECT_EQ("216", message.repeated_bytes   (1));
+
+  EXPECT_EQ(217, message.repeatedgroup           (1).a());
+  EXPECT_EQ(218, message.repeated_nested_message (1).bb());
+  EXPECT_EQ(219, message.repeated_foreign_message(1).c());
+  EXPECT_EQ(220, message.repeated_import_message (1).d());
+
+  EXPECT_EQ(unittest::TestAllTypes::BAR, message.repeated_nested_enum (1));
+  EXPECT_EQ(unittest::FOREIGN_BAR      , message.repeated_foreign_enum(1));
+  EXPECT_EQ(unittest_import::IMPORT_BAR, message.repeated_import_enum (1));
+
+  EXPECT_EQ(301  , message.repeated_int32   (0));
+  EXPECT_EQ(302  , message.repeated_int64   (0));
+  EXPECT_EQ(303  , message.repeated_uint32  (0));
+  EXPECT_EQ(304  , message.repeated_uint64  (0));
+  EXPECT_EQ(305  , message.repeated_sint32  (0));
+  EXPECT_EQ(306  , message.repeated_sint64  (0));
+  EXPECT_EQ(307  , message.repeated_fixed32 (0));
+  EXPECT_EQ(308  , message.repeated_fixed64 (0));
+  EXPECT_EQ(309  , message.repeated_sfixed32(0));
+  EXPECT_EQ(310  , message.repeated_sfixed64(0));
+  EXPECT_EQ(311  , message.repeated_float   (0));
+  EXPECT_EQ(312  , message.repeated_double  (0));
+  EXPECT_EQ(false, message.repeated_bool    (0));
+  EXPECT_EQ("315", message.repeated_string  (0));
+  EXPECT_EQ("316", message.repeated_bytes   (0));
+
+  EXPECT_EQ(317, message.repeatedgroup           (0).a());
+  EXPECT_EQ(318, message.repeated_nested_message (0).bb());
+  EXPECT_EQ(319, message.repeated_foreign_message(0).c());
+  EXPECT_EQ(320, message.repeated_import_message (0).d());
+
+  EXPECT_EQ(unittest::TestAllTypes::BAZ, message.repeated_nested_enum (0));
+  EXPECT_EQ(unittest::FOREIGN_BAZ      , message.repeated_foreign_enum(0));
+  EXPECT_EQ(unittest_import::IMPORT_BAZ, message.repeated_import_enum (0));
+}
+
+void TestUtil::ExpectRepeatedExtensionsSwapped(
+    const unittest::TestAllExtensions& message) {
+
+  ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_int32_extension   ));
+  ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_int64_extension   ));
+  ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_uint32_extension  ));
+  ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_uint64_extension  ));
+  ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_sint32_extension  ));
+  ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_sint64_extension  ));
+  ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_fixed32_extension ));
+  ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_fixed64_extension ));
+  ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_sfixed32_extension));
+  ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_sfixed64_extension));
+  ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_float_extension   ));
+  ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_double_extension  ));
+  ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_bool_extension    ));
+  ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_string_extension  ));
+  ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_bytes_extension   ));
+
+  ASSERT_EQ(2, message.ExtensionSize(unittest::repeatedgroup_extension           ));
+  ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_nested_message_extension ));
+  ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_foreign_message_extension));
+  ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_import_message_extension ));
+  ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_nested_enum_extension    ));
+  ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_foreign_enum_extension   ));
+  ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_import_enum_extension    ));
+
+  ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_string_piece_extension));
+  ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_cord_extension));
+
+  EXPECT_EQ(201  , message.GetExtension(unittest::repeated_int32_extension   , 1));
+  EXPECT_EQ(202  , message.GetExtension(unittest::repeated_int64_extension   , 1));
+  EXPECT_EQ(203  , message.GetExtension(unittest::repeated_uint32_extension  , 1));
+  EXPECT_EQ(204  , message.GetExtension(unittest::repeated_uint64_extension  , 1));
+  EXPECT_EQ(205  , message.GetExtension(unittest::repeated_sint32_extension  , 1));
+  EXPECT_EQ(206  , message.GetExtension(unittest::repeated_sint64_extension  , 1));
+  EXPECT_EQ(207  , message.GetExtension(unittest::repeated_fixed32_extension , 1));
+  EXPECT_EQ(208  , message.GetExtension(unittest::repeated_fixed64_extension , 1));
+  EXPECT_EQ(209  , message.GetExtension(unittest::repeated_sfixed32_extension, 1));
+  EXPECT_EQ(210  , message.GetExtension(unittest::repeated_sfixed64_extension, 1));
+  EXPECT_EQ(211  , message.GetExtension(unittest::repeated_float_extension   , 1));
+  EXPECT_EQ(212  , message.GetExtension(unittest::repeated_double_extension  , 1));
+  EXPECT_EQ(true , message.GetExtension(unittest::repeated_bool_extension    , 1));
+  EXPECT_EQ("215", message.GetExtension(unittest::repeated_string_extension  , 1));
+  EXPECT_EQ("216", message.GetExtension(unittest::repeated_bytes_extension   , 1));
+
+  EXPECT_EQ(217, message.GetExtension(unittest::repeatedgroup_extension           , 1).a());
+  EXPECT_EQ(218, message.GetExtension(unittest::repeated_nested_message_extension , 1).bb());
+  EXPECT_EQ(219, message.GetExtension(unittest::repeated_foreign_message_extension, 1).c());
+  EXPECT_EQ(220, message.GetExtension(unittest::repeated_import_message_extension , 1).d());
+
+  EXPECT_EQ(unittest::TestAllTypes::BAR, message.GetExtension(unittest::repeated_nested_enum_extension , 1));
+  EXPECT_EQ(unittest::FOREIGN_BAR      , message.GetExtension(unittest::repeated_foreign_enum_extension, 1));
+  EXPECT_EQ(unittest_import::IMPORT_BAR, message.GetExtension(unittest::repeated_import_enum_extension , 1));
+
+  EXPECT_EQ("224", message.GetExtension(unittest::repeated_string_piece_extension, 1));
+  EXPECT_EQ("225", message.GetExtension(unittest::repeated_cord_extension, 1));
+
+  EXPECT_EQ(301  , message.GetExtension(unittest::repeated_int32_extension   , 0));
+  EXPECT_EQ(302  , message.GetExtension(unittest::repeated_int64_extension   , 0));
+  EXPECT_EQ(303  , message.GetExtension(unittest::repeated_uint32_extension  , 0));
+  EXPECT_EQ(304  , message.GetExtension(unittest::repeated_uint64_extension  , 0));
+  EXPECT_EQ(305  , message.GetExtension(unittest::repeated_sint32_extension  , 0));
+  EXPECT_EQ(306  , message.GetExtension(unittest::repeated_sint64_extension  , 0));
+  EXPECT_EQ(307  , message.GetExtension(unittest::repeated_fixed32_extension , 0));
+  EXPECT_EQ(308  , message.GetExtension(unittest::repeated_fixed64_extension , 0));
+  EXPECT_EQ(309  , message.GetExtension(unittest::repeated_sfixed32_extension, 0));
+  EXPECT_EQ(310  , message.GetExtension(unittest::repeated_sfixed64_extension, 0));
+  EXPECT_EQ(311  , message.GetExtension(unittest::repeated_float_extension   , 0));
+  EXPECT_EQ(312  , message.GetExtension(unittest::repeated_double_extension  , 0));
+  EXPECT_EQ(false, message.GetExtension(unittest::repeated_bool_extension    , 0));
+  EXPECT_EQ("315", message.GetExtension(unittest::repeated_string_extension  , 0));
+  EXPECT_EQ("316", message.GetExtension(unittest::repeated_bytes_extension   , 0));
+
+  EXPECT_EQ(317, message.GetExtension(unittest::repeatedgroup_extension           , 0).a());
+  EXPECT_EQ(318, message.GetExtension(unittest::repeated_nested_message_extension , 0).bb());
+  EXPECT_EQ(319, message.GetExtension(unittest::repeated_foreign_message_extension, 0).c());
+  EXPECT_EQ(320, message.GetExtension(unittest::repeated_import_message_extension , 0).d());
+
+  EXPECT_EQ(unittest::TestAllTypes::BAZ, message.GetExtension(unittest::repeated_nested_enum_extension , 0));
+  EXPECT_EQ(unittest::FOREIGN_BAZ      , message.GetExtension(unittest::repeated_foreign_enum_extension, 0));
+  EXPECT_EQ(unittest_import::IMPORT_BAZ, message.GetExtension(unittest::repeated_import_enum_extension , 0));
+
+  EXPECT_EQ("324", message.GetExtension(unittest::repeated_string_piece_extension, 0));
+  EXPECT_EQ("325", message.GetExtension(unittest::repeated_cord_extension, 0));
+}
+
 // ===================================================================
 
 TestUtil::ReflectionTester::ReflectionTester(
@@ -2451,5 +2740,31 @@ void TestUtil::ReflectionTester::ModifyPackedFieldsViaReflection(
   reflection->SetRepeatedEnum  (message, F("packed_enum"    ), 1, foreign_foo_);
 }
 
+void TestUtil::ReflectionTester::RemoveLastRepeatedsViaReflection(Message* message) {
+  const Reflection* reflection = message->GetReflection();
+
+  vector<const FieldDescriptor*> output;
+  reflection->ListFields(*message, &output);
+  for (int i=0; i<output.size(); ++i) {
+    const FieldDescriptor* field = output[i];
+    if (!field->is_repeated()) continue;
+
+    reflection->RemoveLast(message, field);
+  }
+}
+
+void TestUtil::ReflectionTester::SwapRepeatedsViaReflection(Message* message) {
+  const Reflection* reflection = message->GetReflection();
+
+  vector<const FieldDescriptor*> output;
+  reflection->ListFields(*message, &output);
+  for (int i=0; i<output.size(); ++i) {
+    const FieldDescriptor* field = output[i];
+    if (!field->is_repeated()) continue;
+
+    reflection->SwapElements(message, field, 0, 1);
+  }
+}
+
 }  // namespace protobuf
 }  // namespace google

+ 14 - 0
src/google/protobuf/test_util.h

@@ -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);