void ThreadSharedVariant::getStats(SharedVariantStats *stats) {
  stats->initStats();
  stats->variantCount = 1;
  switch (m_type) {
  case KindOfBoolean:
  case KindOfInt64:
    stats->dataSize = sizeof(m_data.num);
    stats->dataTotalSize = sizeof(ThreadSharedVariant);
    break;
  case KindOfDouble:
    stats->dataSize = sizeof(m_data.dbl);
    stats->dataTotalSize = sizeof(ThreadSharedVariant);
    break;
  case KindOfString:
  case KindOfObject:
    if (m_data.str->isStatic()) {
      stats->dataSize = 0;
      stats->dataTotalSize = sizeof(ThreadSharedVariant);
      break;
    }
    stats->dataSize = m_data.str->size();
    stats->dataTotalSize = sizeof(ThreadSharedVariant) + sizeof(StringData) +
                           stats->dataSize;
    break;
  default:
    ASSERT(is(KindOfArray));
    if (getSerializedArray()) {
      stats->dataSize = m_data.str->size();
      stats->dataTotalSize = sizeof(ThreadSharedVariant) + sizeof(StringData) +
                             stats->dataSize;
      break;
    }
    if (getIsVector()) {
      stats->dataTotalSize = sizeof(ThreadSharedVariant) + sizeof(VectorData);
      stats->dataTotalSize += sizeof(ThreadSharedVariant*) * m_data.vec->size;
      for (size_t i = 0; i < m_data.vec->size; i++) {
        ThreadSharedVariant *v = m_data.vec->vals[i];
        SharedVariantStats childStats;
        v->getStats(&childStats);
        stats->addChildStats(&childStats);
      }
    } else {
      ImmutableMap *map = m_data.map;
      stats->dataTotalSize = sizeof(ThreadSharedVariant) + map->getStructSize();
      for (int i = 0; i < map->size(); i++) {
        SharedVariantStats childStats;
        map->getKeyIndex(i)->getStats(&childStats);
        stats->addChildStats(&childStats);
        map->getValIndex(i)->getStats(&childStats);
        stats->addChildStats(&childStats);
      }
    }
    break;
  }
}
SharedVariant* ThreadSharedVariant::convertObj(CVarRef var) {
  if (!var.is(KindOfObject) || getObjAttempted()) {
    return NULL;
  }
  setObjAttempted();
  PointerSet seen;
  ObjectData *obj = var.getObjectData();
  CArrRef arr = obj->o_toArray();
  if (arr->hasInternalReference(seen, true)) {
    return NULL;
  }
  ThreadSharedVariant *tmp = new ThreadSharedVariant(var, false, true, true);
  tmp->setObjAttempted();
  return tmp;
}
void ThreadSharedVariant::getStats(SharedVariantStats *stats) {
  stats->initStats();
  stats->variantCount = 1;
  switch (m_type) {
  case KindOfNull:
  case KindOfBoolean:
  case KindOfInt64:
  case KindOfDouble:
    stats->dataSize = sizeof(m_data.dbl);
    stats->dataTotalSize = sizeof(ThreadSharedVariant);
    break;
  case KindOfObject:
    if (getIsObj()) {
      SharedVariantStats childStats;
      m_data.obj->getSizeStats(&childStats);
      stats->addChildStats(&childStats);
      break;
    }
    // fall through
  case KindOfString:
   if (m_data.str->isStatic()) {
      stats->dataSize = 0;
      stats->dataTotalSize = sizeof(ThreadSharedVariant);
      break;
    }
    stats->dataSize = m_data.str->size();
    stats->dataTotalSize = sizeof(ThreadSharedVariant) + sizeof(StringData) +
                           stats->dataSize;
    break;
  default:
    ASSERT(is(KindOfArray));
    if (getSerializedArray()) {
      stats->dataSize = m_data.str->size();
      stats->dataTotalSize = sizeof(ThreadSharedVariant) + sizeof(StringData) +
                             stats->dataSize;
      break;
    }
    if (getIsVector()) {
      stats->dataTotalSize = sizeof(ThreadSharedVariant) + sizeof(VectorData);
      stats->dataTotalSize += sizeof(ThreadSharedVariant*) * m_data.vec->size;
      for (size_t i = 0; i < m_data.vec->size; i++) {
        ThreadSharedVariant *v = m_data.vec->vals[i];
        SharedVariantStats childStats;
        v->getStats(&childStats);
        stats->addChildStats(&childStats);
      }
    } else if (RuntimeOption::ApcUseGnuMap) {
      // There is no way to calculate this accurately, and this should be only
      // used if ImmutableMap is seriously broken, when profiling is less
      // important. Just not do basic thing here:
      stats->dataTotalSize = sizeof(MapData);
    } else {
      ImmutableMap *map = m_data.map;
      stats->dataTotalSize = sizeof(ThreadSharedVariant) + map->getStructSize();
      for (int i = 0; i < map->size(); i++) {
        SharedVariantStats childStats;
        map->getKeyIndex(i)->getStats(&childStats);
        stats->addChildStats(&childStats);
        map->getValIndex(i)->getStats(&childStats);
        stats->addChildStats(&childStats);
      }
    }
    break;
  }
}
ThreadSharedVariant::ThreadSharedVariant(CVarRef source, bool serialized,
                                         bool inner /* = false */,
                                         bool unserializeObj /* = false */) {
  ASSERT(!serialized || source.isString());

  setOwner();
  m_ref = 1;

  switch (source.getType()) {
  case KindOfBoolean:
    {
      m_type = KindOfBoolean;
      m_data.num = source.toBoolean();
      break;
    }
  case KindOfByte:
  case KindOfInt16:
  case KindOfInt32:
  case KindOfInt64:
    {
      m_type = KindOfInt64;
      m_data.num = source.toInt64();
      break;
    }
  case KindOfDouble:
    {
      m_type = KindOfDouble;
      m_data.dbl = source.toDouble();
      break;
    }
  case KindOfStaticString:
  case KindOfString:
    {
      String s = source.toString();
      m_type = serialized ? KindOfObject : KindOfString;
      if (serialized) {
        // It is priming, and there might not be the right class definitions
        // for unserialization.
        s = apc_reserialize(s);
      }
      m_data.str = s->copy(true);
      break;
    }
  case KindOfArray:
    {
      m_type = KindOfArray;

      ArrayData *arr = source.getArrayData();

      if (!inner) {
        // only need to call hasInternalReference() on the toplevel array
        PointerSet seen;
        if (arr->hasInternalReference(seen)) {
          setSerializedArray();
          setShouldCache();
          String s = apc_serialize(source);
          m_data.str = new StringData(s.data(), s.size(), CopyString);
          break;
        }
      }

      size_t size = arr->size();
      if (arr->isVectorData()) {
        setIsVector();
        m_data.vec = new VectorData(size);
        uint i = 0;
        for (ArrayIter it(arr); !it.end(); it.next(), i++) {
          ThreadSharedVariant* val = Create(it.second(), false, true,
                                            unserializeObj);
          if (val->shouldCache()) setShouldCache();
          m_data.vec->vals[i] = val;
        }
      } else if (RuntimeOption::ApcUseGnuMap) {
        // Configured to use old Gnu Map
        m_data.gnuMap = new MapData(size);
        uint i = 0;
        for (ArrayIter it(arr); !it.end(); it.next(), i++) {
          ThreadSharedVariant* key = Create(it.first(), false, true,
                                            unserializeObj);
          ThreadSharedVariant* val = Create(it.second(), false, true,
                                            unserializeObj);
          if (val->shouldCache()) setShouldCache();
          m_data.gnuMap->set(i, key, val);
        }
      } else {
        m_data.map = new ImmutableMap(size);
        for (ArrayIter it(arr); !it.end(); it.next()) {
          ThreadSharedVariant* key = Create(it.first(), false, true,
                                            unserializeObj);
          ThreadSharedVariant* val = Create(it.second(), false, true,
                                            unserializeObj);
          if (val->shouldCache()) setShouldCache();
          m_data.map->add(key, val);
        }
      }
      break;
    }
  case KindOfNull:
    {
      m_type = KindOfNull;
      m_data.num = 0;
      break;
    }
  default:
    {
      ASSERT(source.isObject());
      m_type = KindOfObject;
      setShouldCache();
      if (unserializeObj) {
        // This assumes hasInternalReference(seen, true) is false
        ImmutableObj* obj = new ImmutableObj(source.getObjectData());
        m_data.obj = obj;
        setIsObj();
      } else {
        String s = apc_serialize(source);
        m_data.str = new StringData(s.data(), s.size(), CopyString);
      }
      break;
    }
  }
}