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/array-data.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_ARRAY_DATA_H_
#define incl_HPHP_ARRAY_DATA_H_

#include <climits>
#include <vector>

#include <folly/Likely.h>

#include "hphp/runtime/base/memory-manager.h"
#include "hphp/runtime/base/countable.h"
#include "hphp/runtime/base/typed-value.h"
#include "hphp/runtime/base/sort-flags.h"
#include "hphp/runtime/base/cap-code.h"
#include "hphp/util/md5.h"

namespace HPHP {
///////////////////////////////////////////////////////////////////////////////

struct Array;
struct String;
struct TypedValue;
struct MArrayIter;
struct VariableSerializer;

// tuple-pod result of lval-style apis.
struct ArrayLval {
  ArrayData* array; // new array, if cow happened.
  Variant* val;     // pointer to lval within new array
};

struct ArrayData : MaybeCountable {
  // Runtime type tag of possible array types.  This is intentionally
  // not an enum class, since we're using it pretty much as raw bits
  // (these tag values are not private), which avoids boilerplate
  // when:
  //  - doing relational comparisons
  //  - using kind as an index
  //  - doing bit ops when storing in the union'd words below
  //
  // Beware if you change the order or the numerical values, as there are
  // a few places in the code that depends on the order or the numeric
  // values. Also, all of the values need to be continuous from 0 to =
  // kNumKinds-1 since we use these values to index into a table.
  enum ArrayKind : uint8_t {
    kPackedKind = 0,  // PackedArray with keys in range [0..size)
    kMixedKind = 1,   // MixedArray arbitrary int or string keys, maybe holes
    kEmptyKind = 2,   // The singleton static empty array
    kApcKind = 3,     // APCLocalArray
    kGlobalsKind = 4, // GlobalsArray
    kProxyKind = 5,   // ProxyArray
    kDictKind = 6,    // MixedArray without implicit conversion of integer-like
                      // string keys
    kVecKind = 7,     // Vector array (more restrictive PackedArray)
    kKeysetKind = 8,  // MixedArray storing its keys as values, no implicit
                      // conversions from integer-like string keys
    kNumKinds = 9     // insert new values before kNumKinds.
  };

protected:
  /*
   * NOTE: MixedArray no longer calls this constructor.  If you change
   * it, change the MixedArray::Make functions as appropriate.
   */
  explicit ArrayData(ArrayKind kind, RefCount initial_count = 1)
    : m_sizeAndPos(uint32_t(-1)) {
    initHeader(static_cast<HeaderKind>(kind), initial_count);
    assert(m_size == -1);
    assert(m_pos == 0);
    assert(m_kind == static_cast<HeaderKind>(kind));
  }

  /*
   * NOTE: MixedArray no longer calls this destructor.  If you need to
   * add logic, revisit MixedArray::Release{,Packed}.
   *
   * Include mixed-array-defs.h if you need the definition of this
   * destructor.  It is inline only.
   */
  ~ArrayData();

public:
  ALWAYS_INLINE void decRefAndRelease() {
    assert(kindIsValid());
    if (decReleaseCheck()) release();
  }

  bool kindIsValid() const {
    return isArrayKind(m_kind);
  }

  /**
   * Create a new ArrayData with specified array element(s).
   */
  static ArrayData *Create();
  static ArrayData *Create(const Variant& value);
  static ArrayData *Create(const Variant& name, const Variant& value);
  static ArrayData *CreateRef(Variant& value);
  static ArrayData *CreateRef(const Variant& name, Variant& value);

  static ArrayData *CreateVec();
  static ArrayData *CreateDict();
  static ArrayData *CreateKeyset();

  /*
   * Called to return an ArrayData to the request heap.  This is
   * normally called when the reference count goes to zero (e.g. via a
   * helper like decRefArr).
   */
  void release() noexcept;

  /**
   * Whether this array has any element.
   */
  bool empty() const {
    return size() == 0;
  }

  /**
   * return the array kind for fast typechecks
   */
  ArrayKind kind() const {
    assert(kindIsValid());
    return static_cast<ArrayKind>(m_kind);
  }

  /*
   * Should int-like string keys be implicitly converted to integers before they
   * are inserted?
   */
  bool useWeakKeys() const { return isPHPArray(); }

  bool convertKey(const StringData* key, int64_t& i) const;

  /*
   * Return the capacity stored in the header. Not to be confused
   * with MixedArray::capacity
   */
  uint32_t cap() const {
    return aux<CapCode>().decode();
  }

  /**
   * Number of elements this array has.
   */
  size_t size() const {
    if (UNLIKELY((int)m_size) < 0) return vsize();
    return m_size;
  }

  // Unlike ArrayData::size(), this function doesn't delegate
  // to the vsize() function, so its more efficient to use this when
  // you know you don't have a GlobalsArray or ProxyArray.
  size_t getSize() const {
    return m_size;
  }

  /**
   * Number of elements this array has.
   */
  size_t vsize() const;

  /*
   * getValueRef() gets a reference to value at position "pos".  You
   * must not change the returned Variant.
   */
  const Variant& getValueRef(ssize_t pos) const;

  /*
   * Return true for array types that don't have COW semantics.
   */
  bool noCopyOnWrite() const;

  bool isPacked() const { return kind() == kPackedKind; }
  bool isMixed() const { return kind() == kMixedKind; }
  bool isApcArray() const { return kind() == kApcKind; }
  bool isGlobalsArray() const { return kind() == kGlobalsKind; }
  bool isProxyArray() const { return kind() == kProxyKind; }
  bool isEmptyArray() const { return kind() == kEmptyKind; }
  bool isDict() const { return kind() == kDictKind; }
  bool isVecArray() const { return kind() == kVecKind; }
  bool isKeyset() const { return kind() == kKeysetKind; }

  bool hasPackedLayout() const { return isPacked() || isVecArray(); }
  bool hasMixedLayout() const { return isMixed() || isDict(); }

  bool isPHPArray() const { return kind() < kDictKind; }
  bool isHackArray() const { return kind() >= kDictKind; }

  DataType toDataType() const {
    auto const k = kind();
    if (k < kDictKind) return KindOfArray;
    if (k == kVecKind) return KindOfVec;
    if (k == kDictKind) return KindOfDict;
    assert(k == kKeysetKind);
    return KindOfKeyset;
  }
  DataType toPersistentDataType() const {
    auto const k = kind();
    if (k < kDictKind) return KindOfPersistentArray;
    if (k == kVecKind) return KindOfPersistentVec;
    if (k == kDictKind) return KindOfPersistentDict;
    assert(k == kKeysetKind);
    return KindOfPersistentKeyset;
  }

  /*
   * Returns whether or not this array contains "vector-like" data.
   * I.e. iteration order produces int keys 0 to m_size-1 in sequence.
   *
   * For non-Packed array types this is generally an O(N) operation
   * right now.
   */
  bool isVectorData() const;

  /**
   * Position-based iterations, implemented using iter_begin,
   * iter_advance, iter_prev, iter_rewind.
   */
  Variant reset();
  Variant prev();
  Variant current() const;
  Variant next();
  Variant end();
  Variant key() const;
  Variant value(int32_t &pos) const;
  Variant each();

  bool isHead()            const { return m_pos == iter_begin(); }
  bool isTail()            const { return m_pos == iter_last(); }
  bool isInvalid()         const { return m_pos == iter_end(); }

  /**
   * Testing whether a key exists.
   */
  bool exists(int64_t k) const;
  bool exists(const StringData* k) const;

  /*
   * Interface for VM helpers.  ArrayData implements generic versions
   * using the other ArrayData api; subclasses may customize methods either
   * by providing a custom static method in g_array_funcs.
   */
  const TypedValue* nvGet(int64_t k) const;
  const TypedValue* nvGet(const StringData* k) const;
  const TypedValue* nvTryGet(int64_t k) const;
  const TypedValue* nvTryGet(const StringData* k) const;
  Cell nvGetKey(ssize_t pos) const;

  // wrappers that call getValueRef()
  Variant getValue(ssize_t pos) const;
  Variant getKey(ssize_t pos) const;

  /**
   * Getting l-value (that Variant pointer) at specified key. Return this if
   * escalation is not needed, or an escalated array data.
   */
  ArrayLval lval(int64_t k, bool copy);
  ArrayLval lval(StringData* k, bool copy);
  ArrayLval lvalRef(int64_t k, bool copy);
  ArrayLval lvalRef(StringData* k, bool copy);

  /**
   * Getting l-value (that Variant pointer) of a new element with the next
   * available integer key. Return this if escalation is not needed, or an
   * escalated array data. Note that adding a new element with the next
   * available integer key may fail, in which case ret is set to point to
   * the lval blackhole (see lvalBlackHole() for details).
   */
  ArrayLval lvalNew(bool copy);
  ArrayLval lvalNewRef(bool copy);

  /**
   * Setting a value at specified key. If "copy" is true, make a copy first
   * then set the value. Return this if escalation is not needed, or an
   * escalated array data.
   */
  ArrayData *set(int64_t k, const Variant& v, bool copy);
  ArrayData *set(StringData* k, const Variant& v, bool copy);
  ArrayData *set(int64_t k, Cell v, bool copy);
  ArrayData *set(StringData* k, Cell v, bool copy);

  ArrayData *setRef(int64_t k, Variant& v, bool copy);
  ArrayData *setRef(StringData* k, Variant& v, bool copy);

  ArrayData* zSet(int64_t k, RefData* r);
  ArrayData* zSet(StringData* k, RefData* r);
  ArrayData* zAppend(RefData* r, int64_t* key_ptr);

  /**
   * The same as set(), but with the precondition that the key does
   * not already exist in this array.  (This is to allow more
   * efficient implementation of this case in some derived classes.)
   */
  ArrayData *add(int64_t k, const Variant& v, bool copy);
  ArrayData *add(StringData* k, const Variant& v, bool copy);

  /**
   * Remove a value at specified key. If "copy" is true, make a copy first
   * then remove the value. Return this if escalation is not needed, or an
   * escalated array data.
   */
  ArrayData *remove(int64_t k, bool copy);
  ArrayData *remove(const StringData* k, bool copy);

  /**
   * Inline accessors that convert keys to StringData* before delegating to
   * the virtual method.  Helpers that take a const Variant& key dispatch
   * to either the StringData* or int64_t key-type helpers.
   */
  bool exists(const String& k) const;
  bool exists(const Variant& k) const;
  const Variant& get(int64_t k, bool error = false) const;
  const Variant& get(const StringData* k, bool error = false) const;
  const Variant& get(const String& k, bool error = false) const;
  const Variant& get(const Variant& k, bool error = false) const;
  ArrayLval lval(const String& k, bool copy);
  ArrayLval lval(const Variant& k, bool copy);
  ArrayLval lvalRef(const String& k, bool copy);
  ArrayLval lvalRef(const Variant& k, bool copy);
  ArrayData *set(const String& k, const Variant& v, bool copy);
  ArrayData *set(const Variant& k, const Variant& v, bool copy);
  ArrayData *set(const StringData*, const Variant&, bool) = delete;
  ArrayData *setRef(const String& k, Variant& v, bool copy);
  ArrayData *setRef(const Variant& k, Variant& v, bool copy);
  ArrayData *setRef(const StringData*, Variant&, bool) = delete;
  ArrayData *add(const String& k, const Variant& v, bool copy);
  ArrayData *add(const Variant& k, const Variant& v, bool copy);
  ArrayData *remove(const String& k, bool copy);
  ArrayData *remove(const Variant& k, bool copy);

  // See the documentation for IterEnd, IterBegin, etc. in array-data.cpp
  ssize_t iter_begin() const;
  ssize_t iter_last() const;
  ssize_t iter_end() const;
  ssize_t iter_advance(ssize_t prev) const;
  ssize_t iter_rewind(ssize_t prev) const;

  /*
   * Checks if a mutable iterator points to a valid element within
   * this array.  The iterator must be associated with this array (see
   * array-iterator.cpp).  This will return false if the iterator
   * points past the last element, or if the iterator points before
   * the first element.
   */
  bool validMArrayIter(const MArrayIter& fp) const;

  /*
   * Advances the mutable iterator to the next element in the array.
   * The iterator must be associated with this array (see
   * array-iterator.cpp).  Returns false if the iterator has moved
   * past the last element, otherwise returns true.
   */
  bool advanceMArrayIter(MArrayIter& fp);

  ArrayData* escalateForSort(SortFunction sort_function);
  void ksort(int sort_flags, bool ascending);
  void sort(int sort_flags, bool ascending);
  void asort(int sort_flags, bool ascending);
  bool uksort(const Variant& cmp_function);
  bool usort(const Variant& cmp_function);
  bool uasort(const Variant& cmp_function);

  /**
   * Make a copy of myself.
   *
   * copyStatic() means not to use the request-scoped heap.
   * It is only implemented for array types that need to be able to go
   * into the static array list.
   */
  ArrayData* copy() const;
  ArrayData* copyWithStrongIterators() const;
  ArrayData* copyStatic() const;

  /**
   * Append a value to the array.  If "copy" is true, make a copy first and then
   * append the value.  Return nullptr if escalation is not needed, or an
   * escalated array data.
   */
  ArrayData* append(Cell v, bool copy);
  ArrayData* appendRef(Variant& v, bool copy);

  /**
   * Similar to append(v, copy), with reference in v preserved.
   */
  ArrayData* appendWithRef(const Variant& v, bool copy);

  ArrayData* plusEq(const ArrayData* elems);
  ArrayData* merge(const ArrayData* elems);

  /**
   * Stack function: pop the last item and return it.
   */
  ArrayData* pop(Variant &value);

  /**
   * Queue function: remove the 1st item and return it.
   */
  ArrayData* dequeue(Variant &value);

  /**
   * Array function: prepend a new item.
   */
  ArrayData* prepend(Cell v, bool copy);

  /**
   * Convert array to Hack arrays and vice-versa.
   */
  ArrayData* toPHPArray(bool copy);
  ArrayData* toDict(bool copy);
  ArrayData* toVec(bool copy);
  ArrayData* toKeyset(bool copy);

  /**
   * Only map classes need this. Re-index all numeric keys to start from 0.
   */
  void renumber();

  void onSetEvalScalar();

  /**
   * Comparisons.
   */
  int compare(const ArrayData *v2) const;
  bool equal(const ArrayData *v2, bool strict) const;

  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*);
  static bool Lt(const ArrayData*, const ArrayData*);
  static bool Lte(const ArrayData*, const ArrayData*);
  static bool Gt(const ArrayData*, const ArrayData*);
  static bool Gte(const ArrayData*, const ArrayData*);
  static int64_t Compare(const ArrayData*, const ArrayData*);

  void setPosition(int32_t p) {
    assert(m_pos == p || !isStatic());
    m_pos = p;
  }
  int32_t getPosition() const { return m_pos; }

  ArrayData *escalate() const;

  using ScalarArrayKey = MD5;
  struct ScalarHash {
    size_t operator()(const ScalarArrayKey& key) const {
      return key.hash();
    }
    size_t hash(const ScalarArrayKey& key) const {
      return key.hash();
    }
    bool equal(const ScalarArrayKey& k1,
               const ScalarArrayKey& k2) const {
      return k1 == k2;
    }
  };

  static ScalarArrayKey GetScalarArrayKey(ArrayData* arr);
  static ScalarArrayKey GetScalarArrayKey(const char* str, size_t sz);
  static ArrayData* GetScalarArray(ArrayData *arr);
  static ArrayData* GetScalarArray(ArrayData *arr, const ScalarArrayKey& key);

  static constexpr size_t offsetofSize() { return offsetof(ArrayData, m_size); }
  static constexpr size_t sizeofSize() { return sizeof(m_size); }

  static const char* kindToString(ArrayKind kind);

  // helpers for IterateV and IterateKV
  template <typename Fn, class... Args>
  ALWAYS_INLINE
  static typename std::enable_if<
    std::is_same<typename std::result_of<Fn(Args...)>::type,
                 void>::value, bool>::type
  call_helper(Fn f, Args&&... args) {
    f(std::forward<Args>(args)...);
    return false;
  }

  template <typename Fn, class... Args>
  ALWAYS_INLINE
  static typename std::enable_if<
    std::is_same<typename std::result_of<Fn(Args...)>::type,
                 bool>::value, bool>::type
  call_helper(Fn f, Args&&... args) {
    return f(std::forward<Args>(args)...);
  }

  template <typename B, class... Args>
  ALWAYS_INLINE
  static typename std::enable_if<std::is_same<B, bool>::value, bool>::type
  call_helper(B f, Args&&... args) {
    return f;
  }

private:
  friend size_t getMemSize(const ArrayData*);

  static bool EqualHelper(const ArrayData*, const ArrayData*, bool);
  static int64_t CompareHelper(const ArrayData*, const ArrayData*);

protected:
  // error-handling helpers
  static const Variant& getNotFound(int64_t k);
  static const Variant& getNotFound(const StringData* k);
  const Variant& getNotFound(int64_t k, bool error) const;
  const Variant& getNotFound(const StringData* k, bool error) const;
  static const Variant& getNotFound(const String& k);
  static const Variant& getNotFound(const Variant& k);

  static bool IsValidKey(const String& k);
  static bool IsValidKey(const Variant& k);
  static bool IsValidKey(const StringData* k) { return k; }

protected:
  friend struct PackedArray;
  friend struct EmptyArray;
  friend struct MixedArray;
  friend struct BaseVector;
  friend struct c_Vector;
  friend struct c_ImmVector;
  friend struct HashCollection;
  friend struct BaseMap;
  friend struct c_Map;
  friend struct c_ImmMap;
  // The following fields are blocked into unions with qwords so we
  // can combine the stores when initializing arrays.  (gcc won't do
  // this on its own.)
  union {
    struct {
      uint32_t m_size;
      int32_t m_pos;
    };
    uint64_t m_sizeAndPos; // careful, m_pos is signed
  };
};

static_assert(ArrayData::kPackedKind == uint8_t(HeaderKind::Packed), "");
static_assert(ArrayData::kMixedKind == uint8_t(HeaderKind::Mixed), "");
static_assert(ArrayData::kEmptyKind == uint8_t(HeaderKind::Empty), "");
static_assert(ArrayData::kApcKind == uint8_t(HeaderKind::Apc), "");
static_assert(ArrayData::kGlobalsKind == uint8_t(HeaderKind::Globals), "");
static_assert(ArrayData::kProxyKind == uint8_t(HeaderKind::Proxy), "");
static_assert(ArrayData::kDictKind == uint8_t(HeaderKind::Dict), "");
static_assert(ArrayData::kVecKind == uint8_t(HeaderKind::VecArray), "");

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

extern std::aligned_storage<sizeof(ArrayData), 16>::type s_theEmptyArray;
extern std::aligned_storage<sizeof(ArrayData), 16>::type s_theEmptyVecArray;
constexpr size_t kEmptyMixedArraySize = 120;
extern std::aligned_storage<kEmptyMixedArraySize, 16>::type s_theEmptyDictArray;
constexpr size_t kEmptySetArraySize = 96;
extern std::aligned_storage<kEmptySetArraySize, 16>::type s_theEmptySetArray;

/*
 * Return the "static empty array".  This is a singleton static array
 * that can be used whenever an empty array is needed.  It has
 * kEmptyKind and uses the functions in empty-array.cpp.
 */
ALWAYS_INLINE ArrayData* staticEmptyArray() {
  void* vp = &s_theEmptyArray;
  return static_cast<ArrayData*>(vp);
}

ALWAYS_INLINE ArrayData* staticEmptyVecArray() {
  void* vp = &s_theEmptyVecArray;
  return static_cast<ArrayData*>(vp);
}

ALWAYS_INLINE ArrayData* staticEmptyDictArray() {
  void* vp = &s_theEmptyDictArray;
  return static_cast<ArrayData*>(vp);
}

ALWAYS_INLINE ArrayData* staticEmptyKeysetArray() {
  void* vp = &s_theEmptySetArray;
  return static_cast<ArrayData*>(vp);
}

ALWAYS_INLINE ArrayData* ArrayData::Create() {
  return staticEmptyArray();
}

ALWAYS_INLINE ArrayData* ArrayData::CreateVec() {
  return staticEmptyVecArray();
}

ALWAYS_INLINE ArrayData* ArrayData::CreateDict() {
  return staticEmptyDictArray();
}

ALWAYS_INLINE ArrayData* ArrayData::CreateKeyset() {
  return staticEmptyKeysetArray();
}

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

/*
 * ArrayFunctions is a hand-built virtual dispatch table.  Each field represents
 * one virtual method with an array of function pointers, one per ArrayKind.
 * There is one global instance of this table.  Arranging it this way allows
 * dispatch to be done with a single indexed load, using kind as the index.
 */
struct ArrayFunctions {
  // NK stands for number of array kinds.
  static auto const NK = size_t{9};
  void (*release[NK])(ArrayData*);
  const TypedValue* (*nvGetInt[NK])(const ArrayData*, int64_t k);
  const TypedValue* (*nvTryGetInt[NK])(const ArrayData*, int64_t k);
  const TypedValue* (*nvGetStr[NK])(const ArrayData*, const StringData* k);
  const TypedValue* (*nvTryGetStr[NK])(const ArrayData*, const StringData* k);
  Cell (*nvGetKey[NK])(const ArrayData*, ssize_t pos);
  ArrayData* (*setInt[NK])(ArrayData*, int64_t k, Cell v, bool copy);
  ArrayData* (*setStr[NK])(ArrayData*, StringData* k, Cell v,
                           bool copy);
  size_t (*vsize[NK])(const ArrayData*);
  const Variant& (*getValueRef[NK])(const ArrayData*, ssize_t pos);
  bool (*isVectorData[NK])(const ArrayData*);
  bool (*existsInt[NK])(const ArrayData*, int64_t k);
  bool (*existsStr[NK])(const ArrayData*, const StringData* k);
  ArrayLval (*lvalInt[NK])(ArrayData*, int64_t k, bool copy);
  ArrayLval (*lvalIntRef[NK])(ArrayData*, int64_t k, bool copy);
  ArrayLval (*lvalStr[NK])(ArrayData*, StringData* k, bool copy);
  ArrayLval (*lvalStrRef[NK])(ArrayData*, StringData* k, bool copy);
  ArrayLval (*lvalNew[NK])(ArrayData*, bool copy);
  ArrayLval (*lvalNewRef[NK])(ArrayData*, bool copy);
  ArrayData* (*setRefInt[NK])(ArrayData*, int64_t k, Variant& v, bool copy);
  ArrayData* (*setRefStr[NK])(ArrayData*, StringData* k, Variant& v, bool copy);
  ArrayData* (*addInt[NK])(ArrayData*, int64_t k, Cell v, bool copy);
  ArrayData* (*addStr[NK])(ArrayData*, StringData* k, Cell v, bool copy);
  ArrayData* (*removeInt[NK])(ArrayData*, int64_t k, bool copy);
  ArrayData* (*removeStr[NK])(ArrayData*, const StringData* k, bool copy);
  ssize_t (*iterBegin[NK])(const ArrayData*);
  ssize_t (*iterLast[NK])(const ArrayData*);
  ssize_t (*iterEnd[NK])(const ArrayData*);
  ssize_t (*iterAdvance[NK])(const ArrayData*, ssize_t pos);
  ssize_t (*iterRewind[NK])(const ArrayData*, ssize_t pos);
  bool (*validMArrayIter[NK])(const ArrayData*, const MArrayIter&);
  bool (*advanceMArrayIter[NK])(ArrayData*, MArrayIter&);
  ArrayData* (*escalateForSort[NK])(ArrayData*, SortFunction);
  void (*ksort[NK])(ArrayData* ad, int sort_flags, bool ascending);
  void (*sort[NK])(ArrayData* ad, int sort_flags, bool ascending);
  void (*asort[NK])(ArrayData* ad, int sort_flags, bool ascending);
  bool (*uksort[NK])(ArrayData* ad, const Variant& cmp_function);
  bool (*usort[NK])(ArrayData* ad, const Variant& cmp_function);
  bool (*uasort[NK])(ArrayData* ad, const Variant& cmp_function);
  ArrayData* (*copy[NK])(const ArrayData*);
  ArrayData* (*copyWithStrongIterators[NK])(const ArrayData*);
  ArrayData* (*copyStatic[NK])(const ArrayData*);
  ArrayData* (*append[NK])(ArrayData*, Cell v, bool copy);
  ArrayData* (*appendRef[NK])(ArrayData*, Variant& v, bool copy);
  ArrayData* (*appendWithRef[NK])(ArrayData*, const Variant& v, bool copy);
  ArrayData* (*plusEq[NK])(ArrayData*, const ArrayData* elems);
  ArrayData* (*merge[NK])(ArrayData*, const ArrayData* elems);
  ArrayData* (*pop[NK])(ArrayData*, Variant& value);
  ArrayData* (*dequeue[NK])(ArrayData*, Variant& value);
  ArrayData* (*prepend[NK])(ArrayData*, Cell v, bool copy);
  void (*renumber[NK])(ArrayData*);
  void (*onSetEvalScalar[NK])(ArrayData*);
  ArrayData* (*escalate[NK])(const ArrayData*);
  ArrayData* (*zSetInt[NK])(ArrayData*, int64_t k, RefData* v);
  ArrayData* (*zSetStr[NK])(ArrayData*, StringData* k, RefData* v);
  ArrayData* (*zAppend[NK])(ArrayData*, RefData* v, int64_t* key_ptr);
  ArrayData* (*toPHPArray[NK])(ArrayData*, bool);
  ArrayData* (*toDict[NK])(ArrayData*, bool);
  ArrayData* (*toVec[NK])(ArrayData*, bool);
  ArrayData* (*toKeyset[NK])(ArrayData*, bool);
};

extern const ArrayFunctions g_array_funcs;

ALWAYS_INLINE
void decRefArr(ArrayData* arr) {
  arr->decRefAndRelease();
}

[[noreturn]] void throwInvalidArrayKeyException(const TypedValue* key,
                                                const ArrayData* ad);
[[noreturn]] void throwInvalidArrayKeyException(const StringData* key,
                                                const ArrayData* ad);
[[noreturn]] void throwOOBArrayKeyException(TypedValue key,
                                            const ArrayData* ad);
[[noreturn]] void throwOOBArrayKeyException(int64_t key,
                                            const ArrayData* ad);
[[noreturn]] void throwOOBArrayKeyException(const StringData* key,
                                            const ArrayData* ad);
[[noreturn]] void throwRefInvalidArrayValueException(const ArrayData* ad);
[[noreturn]] void throwRefInvalidArrayValueException(const Array& arr);
[[noreturn]] void throwInvalidKeysetOperation();
[[noreturn]] void throwInvalidAdditionException(const ArrayData* ad);
[[noreturn]] void throwVecUnsetException();

void raiseHackArrCompatRefBind(int64_t);
void raiseHackArrCompatRefBind(const StringData*);
void raiseHackArrCompatRefNew();
void raiseHackArrCompatRefIter();

void raiseHackArrCompatAdd();

void raiseHackArrCompatArrMixedCmp();

void raiseHackArrCompatMissingIncDec();
void raiseHackArrCompatMissingSetOp();

std::string makeHackArrCompatImplicitArrayKeyMsg(const TypedValue* key);
void raiseHackArrCompatImplicitArrayKey(const TypedValue* key);

///////////////////////////////////////////////////////////////////////////////
}

#endif // incl_HPHP_ARRAY_DATA_H_