/* +----------------------------------------------------------------------+ | HipHop for PHP | +----------------------------------------------------------------------+ | Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) | | Copyright (c) 1998-2010 Zend Technologies Ltd. (http://www.zend.com) | +----------------------------------------------------------------------+ | This source file is subject to version 2.00 of the Zend license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.zend.com/license/2_00.txt. | | If you did not receive a copy of the Zend license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@zend.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #include "hphp/runtime/base/shared_map.h" #include "hphp/runtime/base/type_conversions.h" #include "hphp/runtime/base/array_iterator.h" #include "hphp/runtime/base/array_init.h" #include "hphp/runtime/base/runtime_option.h" #include "hphp/runtime/base/runtime_error.h" namespace HPHP { IMPLEMENT_SMART_ALLOCATION_HOT(SharedMap); /////////////////////////////////////////////////////////////////////////////// HOT_FUNC CVarRef SharedMap::getValueRef(ssize_t pos) const { SharedVariant *sv = getValueImpl(pos); DataType t = sv->getType(); if (!IS_REFCOUNTED_TYPE(t)) return sv->asCVarRef(); if (LIKELY(m_localCache != nullptr)) { assert(unsigned(pos) < size()); TypedValue* tv = &m_localCache[pos]; if (tv->m_type != KindOfUninit) return tvAsCVarRef(tv); } else { static_assert(KindOfUninit == 0, "must be 0 since we use smart_calloc"); m_localCache = (TypedValue*) smart_calloc(size(), sizeof(TypedValue)); } TypedValue* tv = &m_localCache[pos]; tvAsVariant(tv) = sv->toLocal(); assert(tv->m_type != KindOfUninit); return tvAsCVarRef(tv); } HOT_FUNC SharedMap::~SharedMap() { if (m_localCache) { for (TypedValue* tv = m_localCache, *end = tv + size(); tv < end; ++tv) { tvRefcountedDecRef(tv); } smart_free(m_localCache); } } HOT_FUNC void SharedMap::Release(ArrayData* ad) { asSharedMap(ad)->release(); } inline SharedMap* SharedMap::asSharedMap(ArrayData* ad) { assert(ad->kind() == kSharedKind); assert(dynamic_cast(ad)); return static_cast(ad); } inline const SharedMap* SharedMap::asSharedMap(const ArrayData* ad) { assert(ad->kind() == kSharedKind); assert(dynamic_cast(ad)); return static_cast(ad); } ssize_t SharedMap::getIndex(const StringData* k) const { if (isVector()) return -1; return m_map->indexOf(k); } ssize_t SharedMap::getIndex(int64_t k) const { if (isVector()) { if (k < 0 || (size_t)k >= m_vec->m_size) return -1; return k; } return m_map->indexOf(k); } bool SharedMap::isVectorData() const { if (isVector()) return true; const auto n = size(); for (ssize_t i = 0; i < n; i++) { if (m_map->indexOf(i) != i) return false; } return true; } bool SharedMap::exists(const StringData* k) const { if (isVector()) return false; return m_map->indexOf(k) != -1; } bool SharedMap::exists(int64_t k) const { return getIndex(k) != -1; } /* if a2 is modified copy of a1 (i.e. != a1), then release a1 and return a2 */ inline ArrayData* releaseIfCopied(ArrayData* a1, ArrayData* a2) { if (a1 != a2) a1->release(); return a2; } ArrayData *SharedMap::lval(int64_t k, Variant *&ret, bool copy) { ArrayData *escalated = SharedMap::escalate(); return releaseIfCopied(escalated, escalated->lval(k, ret, false)); } ArrayData *SharedMap::lval(StringData* k, Variant *&ret, bool copy) { ArrayData *escalated = SharedMap::escalate(); return releaseIfCopied(escalated, escalated->lval(k, ret, false)); } ArrayData *SharedMap::lvalNew(Variant *&ret, bool copy) { ArrayData *escalated = SharedMap::escalate(); return releaseIfCopied(escalated, escalated->lvalNew(ret, false)); } ArrayData* SharedMap::SetInt(ArrayData* ad, int64_t k, CVarRef v, bool copy) { ArrayData *escalated = asSharedMap(ad)->SharedMap::escalate(); return releaseIfCopied(escalated, escalated->set(k, v, false)); } ArrayData* SharedMap::SetStr(ArrayData* ad, StringData* k, CVarRef v, bool copy) { ArrayData *escalated = asSharedMap(ad)->SharedMap::escalate(); return releaseIfCopied(escalated, escalated->set(k, v, false)); } ArrayData *SharedMap::setRef(int64_t k, CVarRef v, bool copy) { ArrayData *escalated = SharedMap::escalate(); return releaseIfCopied(escalated, escalated->setRef(k, v, false)); } ArrayData *SharedMap::setRef(StringData* k, CVarRef v, bool copy) { ArrayData *escalated = SharedMap::escalate(); return releaseIfCopied(escalated, escalated->setRef(k, v, false)); } ArrayData *SharedMap::remove(int64_t k, bool copy) { ArrayData *escalated = SharedMap::escalate(); return releaseIfCopied(escalated, escalated->remove(k, false)); } ArrayData *SharedMap::remove(const StringData* k, bool copy) { ArrayData *escalated = SharedMap::escalate(); return releaseIfCopied(escalated, escalated->remove(k, false)); } ArrayData *SharedMap::copy() const { return SharedMap::escalate(); } ArrayData *SharedMap::Append(ArrayData* ad, CVarRef v, bool copy) { auto a = asSharedMap(ad); ArrayData *escalated = a->SharedMap::escalate(); return releaseIfCopied(escalated, escalated->append(v, false)); } ArrayData *SharedMap::appendRef(CVarRef v, bool copy) { ArrayData *escalated = SharedMap::escalate(); return releaseIfCopied(escalated, escalated->appendRef(v, false)); } ArrayData *SharedMap::appendWithRef(CVarRef v, bool copy) { ArrayData *escalated = SharedMap::escalate(); return releaseIfCopied(escalated, escalated->appendWithRef(v, false)); } ArrayData *SharedMap::plus(const ArrayData *elems, bool copy) { ArrayData *escalated = SharedMap::escalate(); return releaseIfCopied(escalated, escalated->plus(elems, false)); } ArrayData *SharedMap::merge(const ArrayData *elems, bool copy) { ArrayData *escalated = SharedMap::escalate(); return releaseIfCopied(escalated, escalated->merge(elems, false)); } ArrayData *SharedMap::prepend(CVarRef v, bool copy) { ArrayData *escalated = SharedMap::escalate(); return releaseIfCopied(escalated, escalated->prepend(v, false)); } ArrayData *SharedMap::escalate() const { ArrayData *ret = loadElems(); assert(!ret->isStatic()); return ret; } TypedValue* SharedMap::NvGetInt(const ArrayData* ad, int64_t k) { auto a = asSharedMap(ad); auto index = a->getIndex(k); if (index == -1) return nullptr; return (TypedValue*)&a->getValueRef(index); } TypedValue* SharedMap::NvGetStr(const ArrayData* ad, const StringData* key) { auto a = asSharedMap(ad); auto index = a->getIndex(key); if (index == -1) return nullptr; return (TypedValue*)&a->getValueRef(index); } void SharedMap::NvGetKey(const ArrayData* ad, TypedValue* out, ssize_t pos) { auto a = asSharedMap(ad); if (a->isVector()) { out->m_data.num = pos; out->m_type = KindOfInt64; } else { Variant k = a->m_map->getKey(pos); TypedValue* tv = k.asTypedValue(); // copy w/out clobbering out->_count. out->m_type = tv->m_type; out->m_data.num = tv->m_data.num; if (tv->m_type != KindOfInt64) out->m_data.pstr->incRefCount(); } } ArrayData* SharedMap::escalateForSort() { ArrayData *ret = loadElems(true /* mapInit */); assert(!ret->isStatic()); return ret; } ssize_t SharedMap::vsize() const { assert(false); // should never be called since we set size already. return m_size; } ssize_t SharedMap::iter_begin() const { return !empty() ? 0 : invalid_index; } ssize_t SharedMap::iter_end() const { auto n = size(); return n > 0 ? ssize_t(n - 1) : invalid_index; } ssize_t SharedMap::iter_advance(ssize_t prev) const { assert(prev >= 0 && prev < size()); ssize_t next = prev + 1; return next < size() ? next : invalid_index; } ssize_t SharedMap::iter_rewind(ssize_t prev) const { assert(prev >= 0 && prev < size()); ssize_t next = prev - 1; return next >= 0 ? next : invalid_index; } bool SharedMap::validFullPos(const FullPos& fp) const { assert(fp.getContainer() == (ArrayData*)this); return false; } bool SharedMap::advanceFullPos(FullPos& fp) { return false; } ArrayData* SharedMap::loadElems(bool mapInit /* = false */) const { uint count = size(); bool isVec = isVector(); auto ai = mapInit ? ArrayInit(count, ArrayInit::mapInit) : isVec ? ArrayInit(count, ArrayInit::vectorInit) : ArrayInit(count); if (isVec) { for (uint i = 0; i < count; i++) { ai.set(getValueRef(i)); } } else { for (uint i = 0; i < count; i++) { ai.add(m_map->getKey(i), getValueRef(i), true); } } ArrayData* elems = ai.create(); if (elems->isStatic()) elems = elems->copy(); return elems; } void SharedMap::getChildren(std::vector &out) { if (m_localCache) { TypedValue *localCacheEnd = m_localCache + size(); for (TypedValue *tv = m_localCache; tv < localCacheEnd; ++tv) { if (tv->m_type != KindOfUninit) { out.push_back(tv); } } } } /////////////////////////////////////////////////////////////////////////////// }