size_t WireFormat::FieldDataOnlyByteSize(const FieldDescriptor* field,
                                         const Message& message) {
  const Reflection* message_reflection = message.GetReflection();

  size_t data_size = 0;

  if (field->is_map()) {
    const MapFieldBase* map_field =
        message_reflection->GetMapData(message, field);
    if (map_field->IsMapValid()) {
      MapIterator iter(const_cast<Message*>(&message), field);
      MapIterator end(const_cast<Message*>(&message), field);
      const FieldDescriptor* key_field = field->message_type()->field(0);
      const FieldDescriptor* value_field = field->message_type()->field(1);
      for (map_field->MapBegin(&iter), map_field->MapEnd(&end); iter != end;
           ++iter) {
        size_t size = kMapEntryTagByteSize;
        size += MapKeyDataOnlyByteSize(key_field, iter.GetKey());
        size += MapValueRefDataOnlyByteSize(value_field, iter.GetValueRef());
        data_size += WireFormatLite::LengthDelimitedSize(size);
      }
      return data_size;
    }
  }

  size_t count = 0;
  if (field->is_repeated()) {
    count =
        internal::FromIntSize(message_reflection->FieldSize(message, field));
  } else if (field->containing_type()->options().map_entry()) {
    // Map entry fields always need to be serialized.
    count = 1;
  } else if (message_reflection->HasField(message, field)) {
    count = 1;
  }

  switch (field->type()) {
#define HANDLE_TYPE(TYPE, TYPE_METHOD, CPPTYPE_METHOD)                      \
  case FieldDescriptor::TYPE_##TYPE:                                        \
    if (field->is_repeated()) {                                             \
      for (size_t j = 0; j < count; j++) {                                  \
        data_size += WireFormatLite::TYPE_METHOD##Size(                     \
            message_reflection->GetRepeated##CPPTYPE_METHOD(message, field, \
                                                            j));            \
      }                                                                     \
    } else {                                                                \
      data_size += WireFormatLite::TYPE_METHOD##Size(                       \
          message_reflection->Get##CPPTYPE_METHOD(message, field));         \
    }                                                                       \
    break;

#define HANDLE_FIXED_TYPE(TYPE, TYPE_METHOD)                   \
  case FieldDescriptor::TYPE_##TYPE:                           \
    data_size += count * WireFormatLite::k##TYPE_METHOD##Size; \
    break;

    HANDLE_TYPE(INT32, Int32, Int32)
    HANDLE_TYPE(INT64, Int64, Int64)
    HANDLE_TYPE(SINT32, SInt32, Int32)
    HANDLE_TYPE(SINT64, SInt64, Int64)
    HANDLE_TYPE(UINT32, UInt32, UInt32)
    HANDLE_TYPE(UINT64, UInt64, UInt64)

    HANDLE_FIXED_TYPE(FIXED32, Fixed32)
    HANDLE_FIXED_TYPE(FIXED64, Fixed64)
    HANDLE_FIXED_TYPE(SFIXED32, SFixed32)
    HANDLE_FIXED_TYPE(SFIXED64, SFixed64)

    HANDLE_FIXED_TYPE(FLOAT, Float)
    HANDLE_FIXED_TYPE(DOUBLE, Double)

    HANDLE_FIXED_TYPE(BOOL, Bool)

    HANDLE_TYPE(GROUP, Group, Message)
    HANDLE_TYPE(MESSAGE, Message, Message)
#undef HANDLE_TYPE
#undef HANDLE_FIXED_TYPE

    case FieldDescriptor::TYPE_ENUM: {
      if (field->is_repeated()) {
        for (size_t j = 0; j < count; j++) {
          data_size += WireFormatLite::EnumSize(
              message_reflection->GetRepeatedEnum(message, field, j)->number());
        }
      } else {
        data_size += WireFormatLite::EnumSize(
            message_reflection->GetEnum(message, field)->number());
      }
      break;
    }

    // Handle strings separately so that we can get string references
    // instead of copying.
    case FieldDescriptor::TYPE_STRING:
    case FieldDescriptor::TYPE_BYTES: {
      for (size_t j = 0; j < count; j++) {
        std::string scratch;
        const std::string& value =
            field->is_repeated()
                ? message_reflection->GetRepeatedStringReference(message, field,
                                                                 j, &scratch)
                : message_reflection->GetStringReference(message, field,
                                                         &scratch);
        data_size += WireFormatLite::StringSize(value);
      }
      break;
    }
  }
  return data_size;
}