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/ref-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_REF_DATA_H
#define incl_HPHP_REF_DATA_H

#include "hphp/runtime/base/countable.h"
#include "hphp/runtime/base/memory-manager.h"
#include "hphp/runtime/base/typed-value.h"

namespace HPHP {

struct Variant;

struct RefBits {
  // only need 1 bit each for cow and z, but filling out the bitfield
  // and assigning all field members at the same time causes causes
  // gcc and clang to coalesce mutations into byte-sized ops.
  union {
    uint16_t bits;
    struct {
      mutable uint8_t cow:1;
      mutable uint8_t z:7;
    };
  };
  explicit operator uint16_t() const { return bits; }
};

/*
 * We heap allocate a RefData when we make a reference to something.
 * A Variant or TypedValue can be KindOfRef and point to a RefData,
 * but the value held here must not be KindOfRef.
 *
 * RefData's are also used to implement static locals, but in this
 * case the RefData itself is allocated in RDS rather than on
 * the heap.  Note that generally speaking a RefData should never
 * contain KindOfUninit, *except* uninitialized RefDatas for this
 * RDS case.
 *
 * RefDatas are also used by the PHP extension compatibility layer to
 * represent "zvals". Because zvals can be shared by multiple things
 * "by value", it was necessary to add fields to RefData to support
 * this. As a consequence, the count field is not the "real"
 * refcount of a RefData - instead the real refcount can be computed
 * by calling getRealCount() (which simply adds the count and cow
 * fields together). When the count field is decremented to 0, the
 * release() method gets called. This will either free the RefData
 * (if the "real" refcount has reached 0) or it will update the
 * count and cow fields appropriately.
 *
 * For more info on the PHP extension compatibility layer, check out
 * the documentation at "doc/php.extension.compat.layer".
 */
struct RefData final : Countable, type_scan::MarkScannableCountable<RefData> {
  /*
   * Some RefData's (static locals) are allocated in RDS, and
   * live until the end of the request.  In this case, we start with a
   * reference count to keep it alive.
   *
   * Note that the JIT accesses RDS RefDatas directly---if you need to
   * change how initialization works it keep that up to date.
   */
  void initInRDS() {
    initHeader<RefBits>({{0}}, HeaderKind::Ref, 1); // cow=z=0, count=1
  }

  /*
   * Create a RefData, allocated in the request local heap.
   */
  static RefData* Make(TypedValue tv) {
    return new (MM().mallocSmallSize(sizeof(RefData)))
      RefData(tv.m_type, tv.m_data.num);
  }

  ~RefData();

  /*
   * Deallocate a RefData.
   */
  void release() noexcept {
    assert(kindIsValid());
    auto& bits = aux<RefBits>();
    if (UNLIKELY(bits.cow)) {
      m_count = 1;
      bits.cow = bits.z = 0;
      return;
    }
    this->~RefData();
    MM().freeSmallSize(this, sizeof(RefData));
  }

  void releaseMem() const {
    MM().freeSmallSize(const_cast<RefData*>(this), sizeof(RefData));
  }

  ALWAYS_INLINE void decRefAndRelease() {
    assert(kindIsValid());
    if (decReleaseCheck()) release();
  }
  bool kindIsValid() const { return m_kind == HeaderKind::Ref; }

  /*
   * Note, despite the name, this can never return a non-Cell.
   */
  const Cell* tv() const {
    assert(kindIsValid());
    return &m_tv;
  }
  Cell* tv() {
    assert(kindIsValid());
    return &m_tv;
  }

  const Variant* var() const {
    return reinterpret_cast<const Variant*>(tv());
  }
  Variant* var() {
    return reinterpret_cast<Variant*>(tv());
  }

  static constexpr int tvOffset() { return offsetof(RefData, m_tv); }
  static constexpr int cowZOffset() { return offsetof(RefData, m_aux16); }

  void assertValid() const { assert(kindIsValid()); }

  int32_t getRealCount() const {
    auto bits = aux<RefBits>();
    assert(kindIsValid());
    assert(bits.cow == 0 || (bits.cow == 1 && m_count >= 1));
    return m_count + bits.cow;
  }

  bool isReferenced() const {
    assert(kindIsValid());
    auto bits = aux<RefBits>();
    assert(bits.cow == 0 || (bits.cow == 1 && m_count >= 1));
    return m_count >= 2 && !bits.cow;
  }

  /**
   * APIs to support the Zend emulation layer
   */

  // Default constructor, provided so that the PHP extension compatibility
  // layer can stack-allocate RefDatas when needed
  RefData() {
    initHeader<RefBits>({{0}}, HeaderKind::Ref, 0); // cow=z=0, count=0
    m_tv.m_type = KindOfNull;
    assert(!aux<RefBits>().cow && !aux<RefBits>().z);
  }

  bool zIsRef() const {
    assert(kindIsValid());
    auto bits = aux<RefBits>();
    assert(bits.cow == 0 || (bits.cow == 1 && m_count >= 1));
    return !bits.cow && (m_count >= 2 || bits.z);
  }

  void zSetIsRef() const {
    auto& bits = aux<RefBits>();
    auto realCount = getRealCount();
    if (realCount >= 2) {
      m_count = realCount;
      bits.cow = bits.z = 0;
    } else {
      assert(!bits.cow);
      bits.cow = 0;
      bits.z = 1;
    }
  }

  void zUnsetIsRef() const {
    auto& bits = aux<RefBits>();
    auto realCount = getRealCount();
    if (realCount >= 2) {
      m_count = realCount - 1;
      bits.cow = 1;
      bits.z = 0;
    } else {
      assert(!bits.cow);
      bits.cow = bits.z = 0;
    }
  }

  void zSetIsRefTo(int val) const {
    if (val) {
      zSetIsRef();
    } else {
      zUnsetIsRef();
    }
  }

  int32_t zRefcount() {
    return getRealCount();
  }

  void zAddRef() {
    if (getRealCount() != 1) {
      ++m_count;
      return;
    }
    auto& bits = aux<RefBits>();
    assert(!bits.cow);
    assert(bits.z < 2);
    m_count = bits.z + 1;
    bits.cow = !bits.z;
    bits.z = 0;
  }

  void zDelRef() {
    if (getRealCount() != 2) {
      assert(getRealCount() != 0);
      --m_count;
      return;
    }
    auto& bits = aux<RefBits>();
    m_count = 1;
    bits.z = !bits.cow;
    bits.cow = 0;
  }

  void zSetRefcount(int val) {
    assert(kindIsValid());
    if (val < 0) {
      val = 0;
    }
    bool zeroOrOne = (val <= 1);
    bool isRef = zIsRef();
    auto& bits = aux<RefBits>();
    bits.cow = !zeroOrOne && !isRef;
    bits.z = zeroOrOne && isRef;
    m_count = val - bits.cow;
    assert(zRefcount() == val);
    assert(zIsRef() == isRef);
  }

  void zInit() {
    auto& bits = aux<RefBits>();
    m_count = 1;
    bits.cow = bits.z = 0;
    m_tv.m_type = KindOfNull;
    m_tv.m_data.num = 0;
  }

private:
  RefData(DataType t, int64_t datum) {
    // Initialize this value by laundering uninitNull -> Null.
    initHeader<RefBits>({{0}}, HeaderKind::Ref, 1); // cow=z=0, count=1
    if (!isNullType(t)) {
      m_tv.m_type = t;
      m_tv.m_data.num = datum;
    } else {
      m_tv.m_type = KindOfNull;
    }
  }

private:
  TypedValue m_tv;
};

ALWAYS_INLINE void decRefRef(RefData* ref) {
  ref->decRefAndRelease();
}

} // namespace HPHP

#endif //incl_HPHP_REF_DATA_H