// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/heap/marking.h"

namespace v8 {
namespace internal {

void Bitmap::Clear() {
  base::Atomic32* cell_base = reinterpret_cast<base::Atomic32*>(cells());
  for (int i = 0; i < CellsCount(); i++) {
    base::Relaxed_Store(cell_base + i, 0);
  }
  // This fence prevents re-ordering of publishing stores with the mark-bit
  // clearing stores.
  base::MemoryFence();
}

void Bitmap::SetRange(uint32_t start_index, uint32_t end_index) {
  unsigned int start_cell_index = start_index >> Bitmap::kBitsPerCellLog2;
  MarkBit::CellType start_index_mask = 1u << Bitmap::IndexInCell(start_index);
  unsigned int end_cell_index = end_index >> Bitmap::kBitsPerCellLog2;
  MarkBit::CellType end_index_mask = 1u << Bitmap::IndexInCell(end_index);
  if (start_cell_index != end_cell_index) {
    // Firstly, fill all bits from the start address to the end of the first
    // cell with 1s.
    SetBitsInCell<AccessMode::ATOMIC>(start_cell_index,
                                      ~(start_index_mask - 1));
    // Then fill all in between cells with 1s.
    base::Atomic32* cell_base = reinterpret_cast<base::Atomic32*>(cells());
    for (unsigned int i = start_cell_index + 1; i < end_cell_index; i++) {
      base::Relaxed_Store(cell_base + i, ~0u);
    }
    // Finally, fill all bits until the end address in the last cell with 1s.
    SetBitsInCell<AccessMode::ATOMIC>(end_cell_index, (end_index_mask - 1));
  } else {
    SetBitsInCell<AccessMode::ATOMIC>(start_cell_index,
                                      end_index_mask - start_index_mask);
  }
  // This fence prevents re-ordering of publishing stores with the mark-
  // bit setting stores.
  base::MemoryFence();
}

void Bitmap::ClearRange(uint32_t start_index, uint32_t end_index) {
  unsigned int start_cell_index = start_index >> Bitmap::kBitsPerCellLog2;
  MarkBit::CellType start_index_mask = 1u << Bitmap::IndexInCell(start_index);

  unsigned int end_cell_index = end_index >> Bitmap::kBitsPerCellLog2;
  MarkBit::CellType end_index_mask = 1u << Bitmap::IndexInCell(end_index);

  if (start_cell_index != end_cell_index) {
    // Firstly, fill all bits from the start address to the end of the first
    // cell with 0s.
    ClearBitsInCell<AccessMode::ATOMIC>(start_cell_index,
                                        ~(start_index_mask - 1));
    // Then fill all in between cells with 0s.
    base::Atomic32* cell_base = reinterpret_cast<base::Atomic32*>(cells());
    for (unsigned int i = start_cell_index + 1; i < end_cell_index; i++) {
      base::Relaxed_Store(cell_base + i, 0);
    }
    // Finally, set all bits until the end address in the last cell with 0s.
    ClearBitsInCell<AccessMode::ATOMIC>(end_cell_index, (end_index_mask - 1));
  } else {
    ClearBitsInCell<AccessMode::ATOMIC>(start_cell_index,
                                        (end_index_mask - start_index_mask));
  }
  // This fence prevents re-ordering of publishing stores with the mark-
  // bit clearing stores.
  base::MemoryFence();
}

bool Bitmap::AllBitsSetInRange(uint32_t start_index, uint32_t end_index) {
  unsigned int start_cell_index = start_index >> Bitmap::kBitsPerCellLog2;
  MarkBit::CellType start_index_mask = 1u << Bitmap::IndexInCell(start_index);

  unsigned int end_cell_index = end_index >> Bitmap::kBitsPerCellLog2;
  MarkBit::CellType end_index_mask = 1u << Bitmap::IndexInCell(end_index);

  MarkBit::CellType matching_mask;
  if (start_cell_index != end_cell_index) {
    matching_mask = ~(start_index_mask - 1);
    if ((cells()[start_cell_index] & matching_mask) != matching_mask) {
      return false;
    }
    for (unsigned int i = start_cell_index + 1; i < end_cell_index; i++) {
      if (cells()[i] != ~0u) return false;
    }
    matching_mask = (end_index_mask - 1);
    // Check against a mask of 0 to avoid dereferencing the cell after the
    // end of the bitmap.
    return (matching_mask == 0) ||
           ((cells()[end_cell_index] & matching_mask) == matching_mask);
  } else {
    matching_mask = end_index_mask - start_index_mask;
    // Check against a mask of 0 to avoid dereferencing the cell after the
    // end of the bitmap.
    return (matching_mask == 0) ||
           (cells()[end_cell_index] & matching_mask) == matching_mask;
  }
}

bool Bitmap::AllBitsClearInRange(uint32_t start_index, uint32_t end_index) {
  unsigned int start_cell_index = start_index >> Bitmap::kBitsPerCellLog2;
  MarkBit::CellType start_index_mask = 1u << Bitmap::IndexInCell(start_index);

  unsigned int end_cell_index = end_index >> Bitmap::kBitsPerCellLog2;
  MarkBit::CellType end_index_mask = 1u << Bitmap::IndexInCell(end_index);

  MarkBit::CellType matching_mask;
  if (start_cell_index != end_cell_index) {
    matching_mask = ~(start_index_mask - 1);
    if ((cells()[start_cell_index] & matching_mask)) return false;
    for (unsigned int i = start_cell_index + 1; i < end_cell_index; i++) {
      if (cells()[i]) return false;
    }
    matching_mask = (end_index_mask - 1);
    // Check against a mask of 0 to avoid dereferencing the cell after the
    // end of the bitmap.
    return (matching_mask == 0) || !(cells()[end_cell_index] & matching_mask);
  } else {
    matching_mask = end_index_mask - start_index_mask;
    // Check against a mask of 0 to avoid dereferencing the cell after the
    // end of the bitmap.
    return (matching_mask == 0) || !(cells()[end_cell_index] & matching_mask);
  }
}

namespace {

void PrintWord(uint32_t word, uint32_t himask = 0) {
  for (uint32_t mask = 1; mask != 0; mask <<= 1) {
    if ((mask & himask) != 0) PrintF("[");
    PrintF((mask & word) ? "1" : "0");
    if ((mask & himask) != 0) PrintF("]");
  }
}

class CellPrinter {
 public:
  CellPrinter() : seq_start(0), seq_type(0), seq_length(0) {}

  void Print(uint32_t pos, uint32_t cell) {
    if (cell == seq_type) {
      seq_length++;
      return;
    }

    Flush();

    if (IsSeq(cell)) {
      seq_start = pos;
      seq_length = 0;
      seq_type = cell;
      return;
    }

    PrintF("%d: ", pos);
    PrintWord(cell);
    PrintF("\n");
  }

  void Flush() {
    if (seq_length > 0) {
      PrintF("%d: %dx%d\n", seq_start, seq_type == 0 ? 0 : 1,
             seq_length * Bitmap::kBitsPerCell);
      seq_length = 0;
    }
  }

  static bool IsSeq(uint32_t cell) { return cell == 0 || cell == 0xFFFFFFFF; }

 private:
  uint32_t seq_start;
  uint32_t seq_type;
  uint32_t seq_length;
};

}  // anonymous namespace

void Bitmap::Print() {
  CellPrinter printer;
  for (int i = 0; i < CellsCount(); i++) {
    printer.Print(i, cells()[i]);
  }
  printer.Flush();
  PrintF("\n");
}

bool Bitmap::IsClean() {
  for (int i = 0; i < CellsCount(); i++) {
    if (cells()[i] != 0) {
      return false;
    }
  }
  return true;
}

}  // namespace internal
}  // namespace v8
