/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef gc_StoreBuffer_inl_h
#define gc_StoreBuffer_inl_h

#include "gc/StoreBuffer.h"

#include "gc/Heap-inl.h"

namespace js {
namespace gc {

inline /* static */ size_t ArenaCellSet::getCellIndex(const TenuredCell* cell) {
  uintptr_t cellOffset = uintptr_t(cell) & ArenaMask;
  MOZ_ASSERT(cellOffset % ArenaCellIndexBytes == 0);
  return cellOffset / ArenaCellIndexBytes;
}

inline /* static */ std::pair<size_t, uint32_t>
ArenaCellSet::getWordIndexAndMask(size_t cellIndex) {
  static_assert(ArenaCellBits::bitsPerElement == 32,
                "unexpected bitsPerElement value");
  return ArenaCellBits::getIndexAndMask(cellIndex);
}

inline bool ArenaCellSet::hasCell(size_t cellIndex) const {
  MOZ_ASSERT(cellIndex < MaxArenaCellIndex);
  return bits.get(cellIndex);
}

inline void ArenaCellSet::putCell(size_t cellIndex) {
  MOZ_ASSERT(cellIndex < MaxArenaCellIndex);
  MOZ_ASSERT(arena);

  bits.set(cellIndex);
  check();
}

inline void ArenaCellSet::check() const {
#ifdef DEBUG
  bool bitsZero = bits.isAllClear();
  MOZ_ASSERT(isEmpty() == bitsZero);
  MOZ_ASSERT(isEmpty() == !arena);
  if (!isEmpty()) {
    MOZ_ASSERT(IsCellPointerValid(arena));
    JSRuntime* runtime = arena->zone()->runtimeFromMainThread();
    uint64_t minorGCCount = runtime->gc.minorGCCount();
    MOZ_ASSERT(minorGCCount == minorGCNumberAtCreation ||
               minorGCCount == minorGCNumberAtCreation + 1);
  }
#endif
}

inline void StoreBuffer::WholeCellBuffer::put(const Cell* cell) {
  if (cell != last_) {
    putDontCheckLast(cell);
  }
}

inline void StoreBuffer::WholeCellBuffer::putDontCheckLast(const Cell* cell) {
  // This can still be called when |cell == last_| if the caller didn't check
  // and that's OK.

  MOZ_ASSERT(cell->isTenured());

  // BigInts don't have any children, so shouldn't show up here.
  MOZ_ASSERT(cell->getTraceKind() != JS::TraceKind::BigInt);

  Arena* arena = cell->asTenured().arena();
  ArenaCellSet* cells = arena->bufferedCells();
  if (cells->isEmpty()) {
    cells = allocateCellSet(arena);
    if (!cells) {
      return;
    }
  }

  cells->putCell(&cell->asTenured());
  cells->check();

  last_ = cell;
}

/* static */
inline bool StoreBuffer::isInWholeCellBuffer(Cell* cell) {
  TenuredCell* tenured = &cell->asTenured();
  gc::ArenaCellSet* cells = tenured->arena()->bufferedCells();
  return cells && cells->hasCell(tenured);
}

inline void StoreBuffer::putWholeCell(Cell* cell) { bufferWholeCell.put(cell); }
inline void StoreBuffer::putWholeCellDontCheckLast(Cell* cell) {
  bufferWholeCell.putDontCheckLast(cell);
}

}  // namespace gc
}  // namespace js

#endif  // gc_StoreBuffer_inl_h
