// @(#)root/meta:$Id$
// Author: Markus Frank 20/05/2005

/*************************************************************************
* Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
* All rights reserved.                                                  *
*                                                                       *
* For the licensing terms see $ROOTSYS/LICENSE.                         *
* For the list of contributors see $ROOTSYS/README/CREDITS.             *
*************************************************************************/

#include "TClass.h"
#include "TError.h"
#include "TInterpreter.h"
#include "TIsAProxy.h"

#include <map>
#include <type_traits>

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TClass                                                               //
//                                                                      //
// TIsAProxy implementation class.                                      //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

namespace {
   struct DynamicType {
      // Helper class to enable typeid on any address
      // Used in code similar to:
      //    typeid( * (DynamicType*) void_ptr );
      virtual ~DynamicType() {}
   };

   typedef std::map<const void*, TClass*> ClassMap_t; // Internal type map
   inline ClassMap_t *GetMap(const void* p)
   {
      return (ClassMap_t*)p;
   }

   inline ClassMap_t::value_type* ToPair(void*p)
   {
      return (ClassMap_t::value_type*)p;
   }
}

//______________________________________________________________________________
TIsAProxy::TIsAProxy(const std::type_info& typ)
   : fType(&typ), fClass(nullptr), fLast(nullptr),
     fSubTypesReaders(0), fSubTypesWriteLockTaken(kFALSE),
     fVirtual(kFALSE), fInit(kFALSE)
{
   // Standard initializing constructor

   static_assert(sizeof(ClassMap_t)<=sizeof(fSubTypes), "ClassMap size is to large for array");

   ::new(fSubTypes) ClassMap_t();
}

//______________________________________________________________________________
TIsAProxy::~TIsAProxy()
{
   // Standard destructor

   ClassMap_t* m = GetMap(fSubTypes);
   m->clear();
   m->~ClassMap_t();
}

//______________________________________________________________________________
void TIsAProxy::SetClass(TClass *cl)
{
   // Set class pointer
   //   This method is not thread safe
   GetMap(fSubTypes)->clear();
   fClass = cl;
   fLast = nullptr;
}

//______________________________________________________________________________
TClass* TIsAProxy::operator()(const void *obj)
{
   // IsA callback

   if ( !fInit )  {
      if ( !fClass.load() && fType ) {
         auto cls = TClass::GetClass(*fType);
         TClass* expected = nullptr;
         fClass.compare_exchange_strong(expected,cls);
      }
      if ( !fClass.load() ) return nullptr;
      fVirtual = (*fClass).ClassProperty() & kClassHasVirtual;
      fInit = kTRUE;
   }
   if ( !obj || !fVirtual )  {
      return fClass.load();
   }
   // Avoid the case that the first word is a virtual_base_offset_table instead of
   // a virtual_function_table
   Long_t offset = **(Long_t**)obj;
   if ( offset == 0 ) {
      return fClass.load();
   }

   DynamicType* ptr = (DynamicType*)obj;
   const std::type_info* typ = &typeid(*ptr);

   if ( typ == fType )  {
     return fClass.load();
   }
   auto last = ToPair(fLast.load());
   if ( last && typ == last->first )  {
      return last->second;
   }
   // Check if type is already in sub-class cache
   last = ToPair(FindSubType(typ));
   if ( last == nullptr || last->second == nullptr )  {
      // Last resort: lookup root class
      auto cls = TClass::GetClass(*typ);
      last = ToPair(CacheSubType(typ,cls));
   }
   fLast.store(last);

   return last == nullptr? nullptr: last->second;
}

//______________________________________________________________________________
inline void* TIsAProxy::FindSubType(const type_info* type) const
{
   // See if we have already cached the TClass that correspond to this type_info.

   bool needToWait = kTRUE;
   do {
     ++fSubTypesReaders;

     //See if there is a writer, if there is we need to release
     // our reader count so that the writer can proceed
     if(fSubTypesWriteLockTaken) {
       --fSubTypesReaders;
       while(fSubTypesWriteLockTaken) {}
     } else {
       needToWait = kFALSE;
     }
   } while(needToWait);

   void* returnValue = nullptr;
   auto const map = GetMap(fSubTypes);

   auto found = map->find(type);
   if(found != map->end()) {
      returnValue = &(*found);
   }
   --fSubTypesReaders;
   return returnValue;
}

//______________________________________________________________________________
void* TIsAProxy::CacheSubType(const type_info* type, TClass* cls)
{
   // Record the TClass found for a type_info, so that we can retrieved it faster.

   //See if another thread has the write lock, wait if it does
   Bool_t expected = kFALSE;
   while(! fSubTypesWriteLockTaken.compare_exchange_strong(expected,kTRUE) ) {
      expected = kFALSE;
   };

   //See if there are any readers
   while(fSubTypesReaders > 0);

   auto map = GetMap(fSubTypes);
   auto ret = map->emplace(type,cls);
   if (!ret.second) {
      // type is already in the map, let's update it.
      (*ret.first).second = cls;
   }

   fSubTypesWriteLockTaken = kFALSE;
   return &(*(ret.first));
}
 TIsAProxy.cxx:1
 TIsAProxy.cxx:2
 TIsAProxy.cxx:3
 TIsAProxy.cxx:4
 TIsAProxy.cxx:5
 TIsAProxy.cxx:6
 TIsAProxy.cxx:7
 TIsAProxy.cxx:8
 TIsAProxy.cxx:9
 TIsAProxy.cxx:10
 TIsAProxy.cxx:11
 TIsAProxy.cxx:12
 TIsAProxy.cxx:13
 TIsAProxy.cxx:14
 TIsAProxy.cxx:15
 TIsAProxy.cxx:16
 TIsAProxy.cxx:17
 TIsAProxy.cxx:18
 TIsAProxy.cxx:19
 TIsAProxy.cxx:20
 TIsAProxy.cxx:21
 TIsAProxy.cxx:22
 TIsAProxy.cxx:23
 TIsAProxy.cxx:24
 TIsAProxy.cxx:25
 TIsAProxy.cxx:26
 TIsAProxy.cxx:27
 TIsAProxy.cxx:28
 TIsAProxy.cxx:29
 TIsAProxy.cxx:30
 TIsAProxy.cxx:31
 TIsAProxy.cxx:32
 TIsAProxy.cxx:33
 TIsAProxy.cxx:34
 TIsAProxy.cxx:35
 TIsAProxy.cxx:36
 TIsAProxy.cxx:37
 TIsAProxy.cxx:38
 TIsAProxy.cxx:39
 TIsAProxy.cxx:40
 TIsAProxy.cxx:41
 TIsAProxy.cxx:42
 TIsAProxy.cxx:43
 TIsAProxy.cxx:44
 TIsAProxy.cxx:45
 TIsAProxy.cxx:46
 TIsAProxy.cxx:47
 TIsAProxy.cxx:48
 TIsAProxy.cxx:49
 TIsAProxy.cxx:50
 TIsAProxy.cxx:51
 TIsAProxy.cxx:52
 TIsAProxy.cxx:53
 TIsAProxy.cxx:54
 TIsAProxy.cxx:55
 TIsAProxy.cxx:56
 TIsAProxy.cxx:57
 TIsAProxy.cxx:58
 TIsAProxy.cxx:59
 TIsAProxy.cxx:60
 TIsAProxy.cxx:61
 TIsAProxy.cxx:62
 TIsAProxy.cxx:63
 TIsAProxy.cxx:64
 TIsAProxy.cxx:65
 TIsAProxy.cxx:66
 TIsAProxy.cxx:67
 TIsAProxy.cxx:68
 TIsAProxy.cxx:69
 TIsAProxy.cxx:70
 TIsAProxy.cxx:71
 TIsAProxy.cxx:72
 TIsAProxy.cxx:73
 TIsAProxy.cxx:74
 TIsAProxy.cxx:75
 TIsAProxy.cxx:76
 TIsAProxy.cxx:77
 TIsAProxy.cxx:78
 TIsAProxy.cxx:79
 TIsAProxy.cxx:80
 TIsAProxy.cxx:81
 TIsAProxy.cxx:82
 TIsAProxy.cxx:83
 TIsAProxy.cxx:84
 TIsAProxy.cxx:85
 TIsAProxy.cxx:86
 TIsAProxy.cxx:87
 TIsAProxy.cxx:88
 TIsAProxy.cxx:89
 TIsAProxy.cxx:90
 TIsAProxy.cxx:91
 TIsAProxy.cxx:92
 TIsAProxy.cxx:93
 TIsAProxy.cxx:94
 TIsAProxy.cxx:95
 TIsAProxy.cxx:96
 TIsAProxy.cxx:97
 TIsAProxy.cxx:98
 TIsAProxy.cxx:99
 TIsAProxy.cxx:100
 TIsAProxy.cxx:101
 TIsAProxy.cxx:102
 TIsAProxy.cxx:103
 TIsAProxy.cxx:104
 TIsAProxy.cxx:105
 TIsAProxy.cxx:106
 TIsAProxy.cxx:107
 TIsAProxy.cxx:108
 TIsAProxy.cxx:109
 TIsAProxy.cxx:110
 TIsAProxy.cxx:111
 TIsAProxy.cxx:112
 TIsAProxy.cxx:113
 TIsAProxy.cxx:114
 TIsAProxy.cxx:115
 TIsAProxy.cxx:116
 TIsAProxy.cxx:117
 TIsAProxy.cxx:118
 TIsAProxy.cxx:119
 TIsAProxy.cxx:120
 TIsAProxy.cxx:121
 TIsAProxy.cxx:122
 TIsAProxy.cxx:123
 TIsAProxy.cxx:124
 TIsAProxy.cxx:125
 TIsAProxy.cxx:126
 TIsAProxy.cxx:127
 TIsAProxy.cxx:128
 TIsAProxy.cxx:129
 TIsAProxy.cxx:130
 TIsAProxy.cxx:131
 TIsAProxy.cxx:132
 TIsAProxy.cxx:133
 TIsAProxy.cxx:134
 TIsAProxy.cxx:135
 TIsAProxy.cxx:136
 TIsAProxy.cxx:137
 TIsAProxy.cxx:138
 TIsAProxy.cxx:139
 TIsAProxy.cxx:140
 TIsAProxy.cxx:141
 TIsAProxy.cxx:142
 TIsAProxy.cxx:143
 TIsAProxy.cxx:144
 TIsAProxy.cxx:145
 TIsAProxy.cxx:146
 TIsAProxy.cxx:147
 TIsAProxy.cxx:148
 TIsAProxy.cxx:149
 TIsAProxy.cxx:150
 TIsAProxy.cxx:151
 TIsAProxy.cxx:152
 TIsAProxy.cxx:153
 TIsAProxy.cxx:154
 TIsAProxy.cxx:155
 TIsAProxy.cxx:156
 TIsAProxy.cxx:157
 TIsAProxy.cxx:158
 TIsAProxy.cxx:159
 TIsAProxy.cxx:160
 TIsAProxy.cxx:161
 TIsAProxy.cxx:162
 TIsAProxy.cxx:163
 TIsAProxy.cxx:164
 TIsAProxy.cxx:165
 TIsAProxy.cxx:166
 TIsAProxy.cxx:167
 TIsAProxy.cxx:168
 TIsAProxy.cxx:169
 TIsAProxy.cxx:170
 TIsAProxy.cxx:171
 TIsAProxy.cxx:172
 TIsAProxy.cxx:173
 TIsAProxy.cxx:174
 TIsAProxy.cxx:175
 TIsAProxy.cxx:176
 TIsAProxy.cxx:177
 TIsAProxy.cxx:178
 TIsAProxy.cxx:179
 TIsAProxy.cxx:180
 TIsAProxy.cxx:181