/** * postSort() runs after the sort has been performed. For HphpArray, postSort() * handles rebuilding the hash. Also, if resetKeys is true, postSort() will * renumber the keys 0 thru n-1. */ void HphpArray::postSort(bool resetKeys) { assert(m_size > 0); auto const ht = hashTab(); initHash(ht, hashSize()); m_hLoad = 0; if (resetKeys) { for (uint32_t pos = 0; pos < m_used; ++pos) { auto& e = data()[pos]; if (e.hasStrKey()) decRefStr(e.key); e.setIntKey(pos); ht[pos] = pos; } m_nextKI = m_size; } else { auto mask = m_tableMask; auto data = this->data(); for (uint32_t pos = 0; pos < m_used; ++pos) { auto& e = data[pos]; auto ei = findForNewInsert(ht, mask, e.hasIntKey() ? e.ikey : e.hash()); *ei = pos; } } m_hLoad = m_size; }
void HashCollection::shrink(uint32_t oldCap /* = 0 */) { assert(isCapacityTooHigh() && (oldCap == 0 || oldCap < cap())); assert(m_size <= posLimit() && posLimit() <= cap()); dropImmCopy(); uint32_t newCap; if (oldCap != 0) { // If an old capacity was specified, use that newCap = oldCap; // .. unless the old capacity is too small, in which case we use the // smallest capacity that is large enough to hold the current number // of elements. for (; newCap < m_size; newCap <<= 1) {} assert(newCap == computeMaxElms(folly::nextPowTwo<uint64_t>(newCap) - 1)); } else { if (m_size == 0 && nextKI() == 0) { decRefArr(m_arr); m_arr = staticEmptyDictArrayAsMixed(); return; } // If no old capacity was provided, we compute the largest capacity // where m_size/cap() is less than or equal to 0.5 for good hysteresis size_t doubleSz = size_t(m_size) * 2; uint32_t capThreshold = (doubleSz < size_t(MaxSize)) ? doubleSz : MaxSize; for (newCap = SmallSize * 2; newCap < capThreshold; newCap <<= 1) {} } assert(SmallSize <= newCap && newCap <= MaxSize); assert(m_size <= newCap); auto* oldAd = arrayData(); if (!oldAd->cowCheck()) { // If the buffer's refcount is 1, we can teleport the elements // to a new buffer auto oldBuf = data(); auto oldUsed = posLimit(); auto oldNextKI = nextKI(); auto arr = MixedArray::asMixed(MixedArray::MakeReserveDict(newCap)); auto data = mixedData(arr); m_arr = arr; auto table = (int32_t*)(data + size_t(newCap)); auto table_mask = tableMask(); arr->m_size = m_size; setPosLimit(m_size); setNextKI(oldNextKI); for (uint32_t frPos = 0, toPos = 0; toPos < m_size; ++toPos, ++frPos) { frPos = skipTombstonesNoBoundsCheck(frPos, oldUsed, oldBuf); copyElm(oldBuf[frPos], data[toPos]); *findForNewInsert(table, table_mask, data[toPos].probe()) = toPos; } oldAd->setZombie(); decRefArr(oldAd); } else { // For cases where the buffer's refcount is greater than 1, call // resizeHelper() resizeHelper(newCap); } assert(canMutateBuffer()); assert(m_immCopy.isNull()); assert(!isCapacityTooHigh() || newCap == oldCap); }
typename std::enable_if< std::is_base_of<BaseSet, TSet>::value, Object>::type BaseSet::php_skip(const Variant& n) { if (!n.isInteger()) { SystemLib::throwInvalidArgumentExceptionObject( "Parameter n must be an integer"); } int64_t len = n.toInt64(); if (len <= 0) { // We know the resulting Set will simply be a copy of this Set, // so we can just call Clone() and return early here. return Object::attach(TSet::Clone(this)); } auto set = req::make<TSet>(); if (len >= m_size) { // We know the resulting Set will be empty, so we can return // early here. return Object{std::move(set)}; } size_t sz = size_t(m_size) - size_t(len); assert(sz); set->reserve(sz); set->setSize(sz); set->setPosLimit(sz); uint32_t frPos = nthElmPos(len); auto table = set->hashTab(); auto mask = set->tableMask(); for (uint32_t toPos = 0; toPos < sz; ++toPos, ++frPos) { while (isTombstone(frPos)) { assert(frPos + 1 < posLimit()); ++frPos; } auto& toE = set->data()[toPos]; dupElm(data()[frPos], toE); *findForNewInsert(table, mask, toE.probe()) = toPos; if (toE.hasIntKey()) { set->updateNextKI(toE.ikey); } else { assert(toE.hasStrKey()); set->updateIntLikeStrKeys(toE.skey); } } return Object{std::move(set)}; }
/** * postSort() runs after the sort has been performed. For MixedArray, postSort() * handles rebuilding the hash. Also, if resetKeys is true, postSort() will * renumber the keys 0 thru n-1. */ void MixedArray::postSort(bool resetKeys) { // nothrow guarantee assert(m_size > 0); auto const ht = hashTab(); initHash(ht, m_scale); if (resetKeys) { for (uint32_t pos = 0; pos < m_used; ++pos) { auto& e = data()[pos]; if (e.hasStrKey()) decRefStr(e.skey); e.setIntKey(pos); ht[pos] = pos; } m_nextKI = m_size; } else { auto mask = this->mask(); auto data = this->data(); for (uint32_t pos = 0; pos < m_used; ++pos) { auto& e = data[pos]; *findForNewInsert(ht, mask, e.probe()) = pos; } } }
ALWAYS_INLINE typename std::enable_if< std::is_base_of<BaseMap, TMap>::value, Object>::type BaseMap::php_take(const Variant& n) { if (!n.isInteger()) { SystemLib::throwInvalidArgumentExceptionObject( "Parameter n must be an integer"); } int64_t len = n.toInt64(); if (len >= int64_t(m_size)) { // We know the resulting Map will simply be a copy of this Map, // so we can just call Clone() and return early here. return Object::attach(TMap::Clone(this)); } auto map = req::make<TMap>(); if (len <= 0) { // We know the resulting Map will be empty, so we can return // early here. return Object{std::move(map)}; } size_t sz = size_t(len); map->reserve(sz); map->setSize(sz); map->setPosLimit(sz); auto table = map->hashTab(); auto mask = map->tableMask(); for (uint32_t frPos = 0, toPos = 0; toPos < sz; ++toPos, ++frPos) { frPos = skipTombstonesNoBoundsCheck(frPos); auto& toE = map->data()[toPos]; dupElm(data()[frPos], toE); *findForNewInsert(table, mask, toE.probe()) = toPos; if (toE.hasIntKey()) { map->updateNextKI(toE.ikey); } else { assert(toE.hasStrKey()); map->updateIntLikeStrKeys(toE.skey); } } return Object{std::move(map)}; }
/** * postSort() runs after the sort has been performed. For HphpArray, postSort() * handles rebuilding the hash. Also, if resetKeys is true, postSort() will * renumber the keys 0 thru n-1. */ void HphpArray::postSort(bool resetKeys) { assert(m_size > 0); size_t tableSize = computeTableSize(m_tableMask); initHash(m_hash, tableSize); m_hLoad = 0; if (resetKeys) { for (uint32_t pos = 0; pos < m_used; ++pos) { Elm* e = &m_data[pos]; if (e->hasStrKey()) decRefStr(e->key); e->setIntKey(pos); m_hash[pos] = pos; } m_nextKI = m_size; } else { for (uint32_t pos = 0; pos < m_used; ++pos) { Elm* e = &m_data[pos]; ElmInd* ei = findForNewInsert(e->hasIntKey() ? e->ikey : e->hash()); *ei = pos; } } m_hLoad = m_size; }
ALWAYS_INLINE typename std::enable_if< std::is_base_of<BaseMap, TMap>::value, Object>::type BaseMap::php_slice(const Variant& start, const Variant& len) { int64_t istart; int64_t ilen; if (!start.isInteger() || (istart = start.toInt64()) < 0) { SystemLib::throwInvalidArgumentExceptionObject( "Parameter start must be a non-negative integer"); } if (!len.isInteger() || (ilen = len.toInt64()) < 0) { SystemLib::throwInvalidArgumentExceptionObject( "Parameter len must be a non-negative integer"); } size_t skipAmt = std::min<size_t>(istart, m_size); size_t sz = std::min<size_t>(ilen, size_t(m_size) - skipAmt); auto map = req::make<TMap>(); map->reserve(sz); map->setSize(sz); map->setPosLimit(sz); uint32_t frPos = nthElmPos(skipAmt); auto table = map->hashTab(); auto mask = map->tableMask(); for (uint32_t toPos = 0; toPos < sz; ++toPos, ++frPos) { frPos = skipTombstonesNoBoundsCheck(frPos); auto& toE = map->data()[toPos]; dupElm(data()[frPos], toE); *findForNewInsert(table, mask, toE.probe()) = toPos; if (toE.hasIntKey()) { map->updateNextKI(toE.ikey); } else { assert(toE.hasStrKey()); map->updateIntLikeStrKeys(toE.skey); } } return Object{std::move(map)}; }
/** * postSort() runs after the sort has been performed. For HphpArray, postSort() * handles rebuilding the hash. Also, if resetKeys is true, postSort() will * renumber the keys 0 thru n-1. */ void HphpArray::postSort(bool resetKeys) { ASSERT(m_size > 0); size_t tableSize = computeTableSize(m_tableMask); initHash(m_hash, tableSize); m_hLoad = 0; if (resetKeys) { for (ElmInd pos = 0; pos <= m_lastE; ++pos) { Elm* e = &m_data[pos]; if (e->hasStrKey() && e->key->decRefCount() == 0) { e->key->release(); } e->setIntKey(pos); m_hash[pos] = pos; } m_nextKI = m_size; } else { for (ElmInd pos = 0; pos <= m_lastE; ++pos) { Elm* e = &m_data[pos]; ElmInd* ei = findForNewInsert(e->hasIntKey() ? e->ikey : e->hash); *ei = pos; } } m_hLoad = m_size; }