/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef ART_RUNTIME_GC_ACCOUNTING_BITMAP_H_
#define ART_RUNTIME_GC_ACCOUNTING_BITMAP_H_

#include <limits.h>
#include <stdint.h>

#include <memory>
#include <set>
#include <vector>

#include "base/bit_utils.h"
#include "base/locks.h"
#include "base/mem_map.h"
#include "runtime_globals.h"

namespace art HIDDEN {

namespace gc {
namespace accounting {

// TODO: Use this code to implement SpaceBitmap.
class Bitmap {
 public:
  // Create and initialize a bitmap with size num_bits. Storage is allocated with a MemMap.
  static Bitmap* Create(const std::string& name, size_t num_bits);

  // Initialize a space bitmap using the provided mem_map as the live bits. Takes ownership of the
  // mem map. The address range covered starts at heap_begin and is of size equal to heap_capacity.
  // Objects are kAlignement-aligned.
  static Bitmap* CreateFromMemMap(MemMap&& mem_map, size_t num_bits);

  // offset is the difference from base to a index.
  static ALWAYS_INLINE constexpr size_t BitIndexToWordIndex(uintptr_t offset) {
    return offset / kBitsPerBitmapWord;
  }

  template<typename T>
  static ALWAYS_INLINE constexpr T WordIndexToBitIndex(T word_index) {
    return static_cast<T>(word_index * kBitsPerBitmapWord);
  }

  static ALWAYS_INLINE constexpr uintptr_t BitIndexToMask(uintptr_t bit_index) {
    return static_cast<uintptr_t>(1) << (bit_index % kBitsPerBitmapWord);
  }

  ALWAYS_INLINE bool SetBit(size_t bit_index) {
    return ModifyBit<true>(bit_index);
  }

  ALWAYS_INLINE bool ClearBit(size_t bit_index) {
    return ModifyBit<false>(bit_index);
  }

  ALWAYS_INLINE bool TestBit(size_t bit_index) const;

  // Returns true if the bit_index was previously set.
  ALWAYS_INLINE bool AtomicTestAndSetBit(size_t bit_index);

  // Fill the bitmap with zeroes.  Returns the bitmap's memory to the system as a side-effect.
  void Clear();

  // Visit the all the set bits range [visit_begin, visit_end) where visit_begin and visit_end are
  // bit indices visitor is called with the index of each set bit.
  template <typename Visitor>
  void VisitSetBits(uintptr_t visit_begin, size_t visit_end, const Visitor& visitor) const;

  void CopyFrom(Bitmap* source_bitmap);

  // Starting address of our internal storage.
  uintptr_t* Begin() const {
    return bitmap_begin_;
  }

  // Size of our bitmap in bits.
  size_t BitmapSize() const { return bitmap_numbits_; }

  // Check that a bit index is valid with a DCHECK.
  ALWAYS_INLINE void CheckValidBitIndex(size_t bit_index) const {
    DCHECK_LT(bit_index, BitmapSize());
  }

  std::string Dump() const;

 protected:
  static constexpr size_t kBitsPerBitmapWord = kBitsPerIntPtrT;

  Bitmap(MemMap&& mem_map, size_t bitmap_size);
  ~Bitmap();

  // Allocate the mem-map for a bitmap based on how many bits are required.
  static MemMap AllocateMemMap(const std::string& name, size_t num_bits);

  template<bool kSetBit>
  ALWAYS_INLINE bool ModifyBit(uintptr_t bit_index);

  // Backing storage for bitmap. This is interpreted as an array of
  // kBitsPerBitmapWord-sized integers, with bits assigned in each word little
  // endian first.
  MemMap mem_map_;

  // This bitmap itself, word sized for efficiency in scanning.
  uintptr_t* const bitmap_begin_;

  // Number of bits in the bitmap.
  size_t bitmap_numbits_;

 private:
  DISALLOW_IMPLICIT_CONSTRUCTORS(Bitmap);
};

// One bit per kAlignment in range [start, end)
template<size_t kAlignment>
class MemoryRangeBitmap : public Bitmap {
 public:
  static MemoryRangeBitmap* Create(
      const std::string& name, uintptr_t cover_begin, uintptr_t cover_end);
  static MemoryRangeBitmap* CreateFromMemMap(
      MemMap&& mem_map, uintptr_t cover_begin, size_t num_bits);

  void SetBitmapSize(size_t bytes) {
    CHECK_ALIGNED(bytes, kAlignment);
    bitmap_numbits_ = bytes / kAlignment;
    size_t rounded_size =
        RoundUp(bitmap_numbits_, kBitsPerBitmapWord) / kBitsPerBitmapWord * sizeof(uintptr_t);
    mem_map_.SetSize(rounded_size);
  }

  // Beginning of the memory range that the bitmap covers.
  ALWAYS_INLINE uintptr_t CoverBegin() const {
    return cover_begin_;
  }

  // End of the memory range that the bitmap covers.
  ALWAYS_INLINE uintptr_t CoverEnd() const {
    return cover_begin_ + kAlignment * BitmapSize();
  }

  // Return the address associated with a bit index.
  ALWAYS_INLINE uintptr_t AddrFromBitIndex(size_t bit_index) const {
    const uintptr_t addr = CoverBegin() + bit_index * kAlignment;
    DCHECK_EQ(BitIndexFromAddr(addr), bit_index);
    return addr;
  }

  // Return the bit index associated with an address .
  ALWAYS_INLINE uintptr_t BitIndexFromAddr(uintptr_t addr) const {
    uintptr_t result = (addr - CoverBegin()) / kAlignment;
    DCHECK(result < BitmapSize()) << CoverBegin() << " <= " <<  addr << " < " << CoverEnd();
    return result;
  }

  ALWAYS_INLINE bool HasAddress(const uintptr_t addr) const {
    // Don't use BitIndexFromAddr() here as the addr passed to this function
    // could be outside the range. If addr < cover_begin_, then the result
    // underflows to some very large value past the end of the bitmap.
    // Therefore, all operations are unsigned here.
    bool ret = (addr - CoverBegin()) / kAlignment < BitmapSize();
    if (ret) {
      DCHECK(CoverBegin() <= addr && addr < CoverEnd())
          << CoverBegin() << " <= " <<  addr << " < " << CoverEnd();
    }
    return ret;
  }

  ALWAYS_INLINE bool Set(uintptr_t addr) {
    return SetBit(BitIndexFromAddr(addr));
  }

  ALWAYS_INLINE bool Clear(uintptr_t addr) {
    return ClearBit(BitIndexFromAddr(addr));
  }

  ALWAYS_INLINE bool Test(uintptr_t addr) const {
    return TestBit(BitIndexFromAddr(addr));
  }

  // Returns true if the object was previously set.
  ALWAYS_INLINE bool AtomicTestAndSet(uintptr_t addr) {
    return AtomicTestAndSetBit(BitIndexFromAddr(addr));
  }

 private:
  MemoryRangeBitmap(MemMap&& mem_map, uintptr_t begin, size_t num_bits)
      : Bitmap(std::move(mem_map), num_bits),
        cover_begin_(begin) {}

  uintptr_t const cover_begin_;

  DISALLOW_IMPLICIT_CONSTRUCTORS(MemoryRangeBitmap);
};

}  // namespace accounting
}  // namespace gc
}  // namespace art

#endif  // ART_RUNTIME_GC_ACCOUNTING_BITMAP_H_