// Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

#ifndef VM_MEMORY_REGION_H_
#define VM_MEMORY_REGION_H_

#include "vm/assert.h"

namespace dart {

// Memory regions are useful for accessing memory with bounds check in
// debug mode. They can be safely passed by value and do not assume ownership
// of the region.
class MemoryRegion {
 public:
  MemoryRegion() : pointer_(NULL), size_(0) { }
  MemoryRegion(void* pointer, uword size) : pointer_(pointer), size_(size) { }

  void* pointer() const { return pointer_; }
  uword size() const { return size_; }
  uword size_in_bits() const { return size_ * kBitsPerByte; }

  static uword pointer_offset() { return OFFSET_OF(MemoryRegion, pointer_); }

  uword start() const { return reinterpret_cast<uword>(pointer_); }
  uword end() const { return start() + size_; }

  template<typename T> T Load(uword offset) const {
    return *ComputeInternalPointer<T>(offset);
  }

  template<typename T> void Store(uword offset, T value) const {
    *ComputeInternalPointer<T>(offset) = value;
  }

  template<typename T> T* PointerTo(uword offset) const {
    return ComputeInternalPointer<T>(offset);
  }

  bool Contains(uword address) const {
    return (address >= start()) && (address < end());
  }

  void CopyFrom(uword offset, const MemoryRegion& from) const;

  // Compute a sub memory region based on an existing one.
  void Subregion(const MemoryRegion& from, uword offset, uword size) {
    ASSERT(from.size() >= size);
    ASSERT(offset <= (from.size() - size));
    pointer_ = reinterpret_cast<void*>(from.start() + offset);
    size_ = size;
  }

  // Compute an extended memory region based on an existing one.
  void Extend(const MemoryRegion& region, uword extra) {
    pointer_ = region.pointer();
    size_ = (region.size() + extra);
  }

 private:
  template<typename T> T* ComputeInternalPointer(uword offset) const {
    ASSERT(size() >= sizeof(T));
    ASSERT(offset <= size() - sizeof(T));
    return reinterpret_cast<T*>(start() + offset);
  }

  // Locate the bit with the given offset. Returns a pointer to the byte
  // containing the bit, and sets bit_mask to the bit within that byte.
  uint8_t* ComputeBitPointer(uword bit_offset, uint8_t* bit_mask) const {
    uword bit_remainder = (bit_offset & (kBitsPerByte - 1));
    *bit_mask = (1U << bit_remainder);
    uword byte_offset = (bit_offset >> kBitsPerByteLog2);
    return ComputeInternalPointer<uint8_t>(byte_offset);
  }

  void* pointer_;
  uword size_;

  DISALLOW_COPY_AND_ASSIGN(MemoryRegion);
};

}  // namespace dart

#endif  // VM_MEMORY_REGION_H_
