HEX
Server: Apache
System: Linux wp02.tdr-lab.com 3.10.0-1160.42.2.el7.x86_64 #1 SMP Tue Sep 7 14:49:57 UTC 2021 x86_64
User: kusanagi (1001)
PHP: 7.4.23
Disabled: NONE
Upload Files
File: //proc/self/root/usr/include/hphp/runtime/base/set-array.h
/*
   +----------------------------------------------------------------------+
   | HipHop for PHP                                                       |
   +----------------------------------------------------------------------+
   | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com)  |
   +----------------------------------------------------------------------+
   | This source file is subject to version 3.01 of the PHP 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.php.net/license/3_01.txt                                  |
   | If you did not receive a copy of the PHP license and are unable to   |
   | obtain it through the world-wide-web, please send a note to          |
   | license@php.net so we can mail you a copy immediately.               |
   +----------------------------------------------------------------------+
*/

#ifndef incl_HPHP_SET_ARRAY_H_
#define incl_HPHP_SET_ARRAY_H_

#include "hphp/runtime/base/array-data.h"
#include "hphp/runtime/base/array-common.h"
#include "hphp/runtime/base/string-data.h"
#include "hphp/runtime/base/typed-value.h"

#include <folly/portability/Constexpr.h>

namespace HPHP {

//////////////////////////////////////////////////////////////////////

namespace jit {
struct ArrayOffsetProfile;
}
struct APCArray;

//////////////////////////////////////////////////////////////////////

struct SetArray final : ArrayData, type_scan::MarkCountable<SetArray> {

//////////////////////////////////////////////////////////////////////
// Set Layout

public:
  struct Elm;

  /*
   * Load factor scaler. If S is the # of elements, C is the
   * power-of-2 capacity, and L=LoadScale, we grow when S > C-C/L.
   * So 2 gives 0.5 load factor, 4 gives 0.75 load factor, 8 gives
   * 0.875 load factor. Use powers of 2 to enable shift-divide.
   *
   * The LoadScale also is the minimum size of the hash table.
   */
  static constexpr uint32_t LoadScale = 4;

  constexpr static uint32_t HashSize(uint32_t scale) { return 4 * scale; }
  constexpr static uint32_t Mask(uint32_t scale) { return HashSize(scale) - 1; }
  constexpr static uint32_t Capacity(uint32_t scale) { return 3 * scale; }
  static_assert(LoadScale == 4, "Change Capacity()");

  /*
   * The minimum hash size for a set is 4.
   */
  constexpr static uint32_t SmallScale = 1;

  uint32_t capacity() const { return Capacity(m_scale); }
  uint32_t mask() const     { return Mask(m_scale); }
  uint32_t scale() const    { return m_scale; }

  /*
   * A set array has a header of type SetArray followed by an array
   * of Capacity(m_scale) Elms, and then a hash table of
   * HashSize(m_scale) uint32_t indices.
   */
  ALWAYS_INLINE static Elm* SetData(const SetArray* a) {
    return const_cast<Elm*>(
      reinterpret_cast<Elm const*>(a + 1)
    );
  }
  Elm* data() const { return SetData(this); }

  ALWAYS_INLINE static uint32_t* SetHashTab(const SetArray* a, uint32_t scale) {
    return const_cast<uint32_t*>(
      reinterpret_cast<uint32_t const*>(SetData(a) + Capacity(scale))
    );
  }
  uint32_t* hashTab() const { return SetHashTab(this, m_scale); }

  constexpr static size_t ComputeAllocBytes(uint32_t scale) {
    return sizeof(SetArray) +
      sizeof(Elm) * Capacity(scale) +
      sizeof(uint32_t) * HashSize(scale);
  }
  size_t heapSize() const { return ComputeAllocBytes(m_scale); }

  /*
   * These two indices are used in hash tables.  It is safe
   * to choose small "negative" integers since the maximum
   * capacity is 3 * 2^30.
   */
  constexpr static uint32_t Empty     = -uint32_t{1};
  constexpr static uint32_t Tombstone = -uint32_t{2};

  void scan(type_scan::Scanner& scanner) const {
    auto const elms = data();
    scanner.scan(*elms, m_used * sizeof(*elms));
  }

//////////////////////////////////////////////////////////////////////
// Initialization, Copies, and Conversions

public:
  /*
   * Allocate a new, empty, request-local set array, with enough space
   * reserved for `size' members.
   *
   * The returned array is already incref'd.
   */
  static ArrayData* MakeReserveSet(uint32_t size);

  static ArrayData* MakeSet(uint32_t size, const TypedValue* values);

  /*
   * Allocate an uncounted SetArray and copy the values from the
   * input 'array' into the uncounted one.
   *
   * 'extra' bytes may be allocated in front of the returned pointer,
   * must be a multiple of 16, and later be passed to ReleaseUncounted.
   * (This is used to co-allocate a TypedValue with its array data.)
   */
  static ArrayData* MakeUncounted(ArrayData* array, size_t extra = 0);

  static void Release(ArrayData*);
  static void ReleaseUncounted(ArrayData*, size_t);

  /*
   * Safe downcast helpers.
   */
  static SetArray* asSet(ArrayData* ad);
  static const SetArray* asSet(const ArrayData* ad);

  static ArrayData* MakeSetFromAPC(const APCArray*);

  /*
   * For array initialization using KeysetInit.
   */
  static ArrayData* AddToSet(ArrayData*, int64_t, bool);
  static ArrayData* AddToSet(ArrayData*, StringData*, bool);

private:
  static void InitHash(uint32_t* table, uint32_t scale);
  static void CopyHash(uint32_t* dest, uint32_t* src, uint32_t scale);
  static bool ClearElms(Elm* elms, uint32_t count);

  enum class AllocMode : bool { Request, Static };

  static SetArray* CopySet(const SetArray& other, AllocMode);
  static SetArray* CopyReserve(const SetArray* src, size_t expectedSize);
  SetArray* copySet() const { return CopySet(*this, AllocMode::Request); }
  SetArray* copyAndResizeIfNeeded() const;

private:
  SetArray() = delete;
  SetArray(const SetArray&) = delete;
  SetArray& operator=(const SetArray&) = delete;
  ~SetArray() = delete;

//////////////////////////////////////////////////////////////////////
// Iteration

private:
  ssize_t getIterBegin() const;
  ssize_t getIterLast() const;
  ssize_t getIterEnd() const { return m_used; }
  Cell getElm(ssize_t ei) const;

  ssize_t nextElm(Elm* elms, ssize_t ei) const;
  ssize_t prevElm(Elm* elms, ssize_t ei) const;
  ssize_t nextElm(ssize_t ei) const { return nextElm(data(), ei); }

public:
  const TypedValue* tvOfPos(uint32_t) const;

  template <class F, bool inc = true>
  static void Iterate(const SetArray* a, F fn) {
    if (inc) a->incRefCount();
    SCOPE_EXIT { if (inc) decRefArr(const_cast<SetArray*>(a)); };
    auto const* elm = a->data();
    for (auto i = a->m_used; i--; elm++) {
      if (LIKELY(!elm->isTombstone())) {
        if (ArrayData::call_helper(fn, &elm->tv)) break;
      }
    }
  }

//////////////////////////////////////////////////////////////////////
// Sorting

public:
  static ArrayData* EscalateForSort(ArrayData* ad, SortFunction);

  static void Sort(ArrayData*, int, bool);
  static void Ksort(ArrayData*, int, bool);
  static constexpr auto Asort = &Ksort;

  static bool Usort(ArrayData*, const Variant&);
  static bool Uksort(ArrayData*, const Variant&);
  static constexpr auto Uasort = &Uksort;

private:
  template <typename AccessorT>
  SortFlavor preSort(const AccessorT& acc, bool checkTypes);
  void postSort();

//////////////////////////////////////////////////////////////////////
// Set Internals

private:
  bool checkInvariants() const;
  bool isFull() const;

  using hash_t = strhash_t;

  /*
   * These functions require !isFull().  The index of the position
   * where the element was inserted is returned.
   */
  void insert(int64_t k, inthash_t h);
  void insert(int64_t k);
  void insert(StringData* k, strhash_t h);
  void insert(StringData* k);

  Elm* allocElm(uint32_t*);

  enum FindType { Lookup, Insert, Remove };

  template <FindType type, class Hit>
  typename std::conditional<
    type == FindType::Lookup,
    ssize_t,
    uint32_t*
  >::type findImpl(hash_t h0, Hit) const;

  ssize_t find(int64_t ki, inthash_t h) const;
  ssize_t find(const StringData* s, strhash_t h) const;
  ssize_t findElm(const Elm& e) const;
  template<FindType t> uint32_t* findHash(int64_t, inthash_t) const;
  template<FindType t> uint32_t* findHash(const StringData*, strhash_t) const;
  uint32_t* findForNewInsert(hash_t h) const;

  void erase(uint32_t*);

  /*
   * Append idx at the end of the linked list containing the set
   * insertion order.
   */
  void linkLast(uint32_t idx);

  /*
   * Helper routine for inserting elements into a new array
   * when grow()ing the array, that also checks for potentially
   * unbalanced entries because of hash collision.
   */
  SetArray* insertCheckUnbalanced(SetArray* ad, Elm* table,
                                  uint32_t mask,
                                  Elm* iter, Elm* stop);


  /*
   * Returns a new set containing all the elements of the current set
   * with the new specified scale.  The original set must not be used
   * afterwards.  If the passed scale is smaller than the original
   * one, grow() can shrink too!
   */
  SetArray* grow(uint32_t newScale);

  /*
   * compact() removes all tombstones from the hash table by going
   * through the inner linked list.
   */
  void compact();

  /*
   * resize() and resizeIfNeeded() will grow the array as necessary to
   * ensure that there is room for a new element and a new hash entry.
   *
   * resize() assumes isFull().  resizeIfNeeded() will first check if
   * there is room for a new element and hash entry before growing the
   * array.
   *
   * Both functions return the new SetArray* to use (or the old one
   * if they didn't need to grow).  The old SetArray is left in a
   * zombie state where the only legal action is to decref and then
   * throw it away.
   */
  SetArray* resize();
  SetArray* resizeIfNeeded();

  /*
   * Zombie arrays!
   */
  bool isZombie() const { return m_used + 1 == 0; }
  void setZombie() { m_used = -uint32_t{1}; }

  /*
   * Comparison helper.
   */
  static bool EqualHelper(const ArrayData*, const ArrayData*, bool);

//////////////////////////////////////////////////////////////////////
// Elements

public:
  struct Elm {
    /*
     * We store elements of the set here, but also some information
     * local to this array: tv.m_aux.u_hash contains either a negative
     * number (for an int key) or a string hashcode (31-bit and thus
     * non-negative).  The field tv.m_type == kInvalidDataType is used
     * to mark a deleted element (tombstone); tv.m_type == KindOfUninit
     * is used for debugging to mark empty elements.
     */
    TypedValueAux tv;

    static auto constexpr kTombstone = kInvalidDataType;
    static auto constexpr kEmpty = KindOfUninit;

    bool hasStrKey() const {
      /*
       * Currently string hash is 31-bit, thus it saves us some
       * instructions to encode int keys as a negative hash, so
       * that we don't have to care about the MSB when working
       * with strhash_t.
       */
      assert(!isInvalid());
      return tv.hash() >= 0;
    }

    bool hasIntKey() const {
      assert(!isInvalid());
      return tv.hash() < 0;
    }

    void setStrKey(StringData* k, strhash_t h) {
      assert(isEmpty());
      k->incRefCount();
      tv.m_type = KindOfString;
      tv.m_data.pstr = k;
      tv.hash() = h;
      assert(!isInvalid());
    }

    StringData* strKey() const {
      assert(hasStrKey());
      return tv.m_data.pstr;
    }

    int64_t intKey() const {
      assert(hasIntKey());
      return tv.m_data.num;
    }

    void setIntKey(int64_t k, inthash_t h) {
      assert(isEmpty());
      tv.m_type = KindOfInt64;
      tv.m_data.num = k;
      tv.hash() = h | STRHASH_MSB;
      assert(!isInvalid());
      assert(hasIntKey());
    }

    void setTombstone() {
      tv.m_type = kTombstone;
    }

    bool isTombstone() const {
      static_assert(
        kEmpty == 0 && kTombstone < 0 &&
        KindOfString > kEmpty && KindOfInt64 > kEmpty,
        "Fix the check below."
      );
      return tv.m_type < kEmpty;
    }

    bool isEmpty() const {
      return tv.m_type == kEmpty;
    }

    bool isInvalid() const {
      // An element is invalid if it is a tombstone or empty.
      static_assert(
        kTombstone < kEmpty &&
        kEmpty < KindOfInt64 &&
        kEmpty < KindOfString &&
        kEmpty < KindOfPersistentString,
        "Revise m_type choices."
      );
      return tv.m_type <= kEmpty;
    }

    hash_t hash() const {
      return tv.hash();
    }
  };

//////////////////////////////////////////////////////////////////////
// JIT Supporting Routines

  static constexpr ptrdiff_t usedOff() {
    return offsetof(SetArray, m_used);
  }

  static constexpr ptrdiff_t dataOff() {
    return sizeof(SetArray);
  }

  static constexpr ptrdiff_t tvOff(uint32_t pos) {
    return dataOff() + pos * sizeof(Elm) + offsetof(Elm, tv);
  }

//////////////////////////////////////////////////////////////////////
// Reference Counting

public:
  using ArrayData::decRefCount;
  using ArrayData::hasMultipleRefs;
  using ArrayData::hasExactlyOneRef;
  using ArrayData::decWillRelease;
  using ArrayData::incRefCount;

//////////////////////////////////////////////////////////////////////
// Misc ArrayData Methods

  /*
   * These using directives ensure the full set of overloaded functions
   * are visible in this class, to avoid triggering implicit conversions
   * from a const Variant& key to int64.
   */
private:
  using ArrayData::exists;
  using ArrayData::lval;
  using ArrayData::lvalNew;
  using ArrayData::set;
  using ArrayData::setRef;
  using ArrayData::add;
  using ArrayData::remove;
  using ArrayData::nvGet;
  using ArrayData::release;

//////////////////////////////////////////////////////////////////////
// Friends

private:
  friend struct ArrayInit;
  friend struct MemoryProfile;
  friend struct jit::ArrayOffsetProfile;
  friend struct EmptyArray;
  friend struct PackedArray;
  friend struct StructArray;
  friend struct MixedArray;
  friend struct HashCollection;
  friend struct BaseMap;
  friend struct c_Map;
  friend struct c_ImmMap;
  friend struct BaseSet;
  friend struct c_Set;
  friend struct c_ImmSet;
  friend struct c_AwaitAllWaitHandle;

  friend size_t getMemSize(const ArrayData*);
  template <typename AccessorT, class ArrayT>
  friend SortFlavor genericPreSort(ArrayT&, const AccessorT&, bool);

//////////////////////////////////////////////////////////////////////
// ArrayData API

public:
  static const TypedValue* NvGetInt(const ArrayData*, int64_t);
  static const TypedValue* NvGetStr(const ArrayData*, const StringData*);
  static const TypedValue* NvTryGetInt(const ArrayData*, int64_t);
  static const TypedValue* NvTryGetStr(const ArrayData*, const StringData*);
  static Cell NvGetKey(const ArrayData*, ssize_t);
  static size_t Vsize(const ArrayData*);
  static const Variant& GetValueRef(const ArrayData*, ssize_t);
  static bool IsVectorData(const ArrayData*);
  static bool ExistsInt(const ArrayData*, int64_t);
  static bool ExistsStr(const ArrayData*, const StringData*);
  static ArrayLval LvalInt(ArrayData*, int64_t, bool);
  static ArrayLval LvalIntRef(ArrayData*, int64_t, bool);
  static ArrayLval LvalStr(ArrayData*, StringData*, bool);
  static ArrayLval LvalStrRef(ArrayData*, StringData*, bool);
  static ArrayLval LvalNew(ArrayData*, bool);
  static ArrayLval LvalNewRef(ArrayData*, bool);
  static ArrayData* SetRefInt(ArrayData*, int64_t, Variant&, bool);
  static ArrayData* SetRefStr(ArrayData*, StringData*, Variant&, bool);
  static ArrayData* SetInt(ArrayData*, int64_t, Cell, bool);
  static ArrayData* SetStr(ArrayData*, StringData*, Cell, bool);
  static ArrayData* RemoveInt(ArrayData*, int64_t, bool);
  static ArrayData* RemoveStr(ArrayData*, const StringData*, bool);
  static ssize_t IterBegin(const ArrayData*);
  static ssize_t IterLast(const ArrayData*);
  static ssize_t IterEnd(const ArrayData*);
  static ssize_t IterAdvance(const ArrayData*, ssize_t);
  static ssize_t IterRewind(const ArrayData*, ssize_t);
  static constexpr auto ValidMArrayIter = &ArrayCommon::ValidMArrayIter;
  static bool AdvanceMArrayIter(ArrayData*, MArrayIter&);
  static ArrayData* Copy(const ArrayData*);
  static ArrayData* CopyWithStrongIterators(const ArrayData*);
  static ArrayData* CopyStatic(const ArrayData*);
  static ArrayData* Append(ArrayData*, Cell, bool);
  static ArrayData* AppendRef(ArrayData*, Variant&, bool);
  static ArrayData* AppendWithRef(ArrayData*, const Variant&, bool);
  static ArrayData* PlusEq(ArrayData*, const ArrayData*);
  static ArrayData* Merge(ArrayData*, const ArrayData*);
  static ArrayData* Pop(ArrayData*, Variant&);
  static ArrayData* Dequeue(ArrayData*, Variant&);
  static ArrayData* Prepend(ArrayData*, Cell, bool);
  static void Renumber(ArrayData*);
  static void OnSetEvalScalar(ArrayData*);
  static ArrayData* Escalate(const ArrayData*);
  static constexpr auto ToDict = &ArrayCommon::ToDict;
  static constexpr auto ToVec = &ArrayCommon::ToVec;
  static ArrayData* ToPHPArray(ArrayData*, bool);
  static ArrayData* ToKeyset(ArrayData*, bool);
  static bool Equal(const ArrayData*, const ArrayData*);
  static bool NotEqual(const ArrayData*, const ArrayData*);
  static bool Same(const ArrayData*, const ArrayData*);
  static bool NotSame(const ArrayData*, const ArrayData*);

//////////////////////////////////////////////////////////////////////

private:
  struct Initializer;
  static Initializer s_initializer;

  /*
   * Some of these are packed into qword-sized unions so we can
   * combine stores during initialization. (gcc won't do it on its own.)
   */

  union {
    struct {
      uint32_t m_scale; // Size-class equal to 1/4 table size.
      uint32_t m_used;  // Number of used entries in the hash.
                        // (includes tombstones)
    };
    uint64_t m_scale_used;
  };
  uint64_t m_padding;
};

//////////////////////////////////////////////////////////////////////

}

#endif // incl_HPHP_SET_ARRAY_H_