// Copyright 2021 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.

#ifndef V8_BASE_VIRTUAL_ADDRESS_SPACE_H_
#define V8_BASE_VIRTUAL_ADDRESS_SPACE_H_

#include "include/v8-platform.h"
#include "src/base/base-export.h"
#include "src/base/compiler-specific.h"
#include "src/base/platform/platform.h"
#include "src/base/region-allocator.h"

namespace v8 {
namespace base {

using Address = uintptr_t;
constexpr Address kNullAddress = 0;

class VirtualAddressSubspace;

/*
 * Common parent class to implement deletion of subspaces.
 */
class VirtualAddressSpaceBase
    : public NON_EXPORTED_BASE(::v8::VirtualAddressSpace) {
 public:
  using VirtualAddressSpace::VirtualAddressSpace;

 private:
  friend VirtualAddressSubspace;
  // Called by a subspace during destruction. Responsible for freeing the
  // address space reservation and any other data associated with the subspace
  // in the parent space.
  virtual void FreeSubspace(VirtualAddressSubspace* subspace) = 0;
};

/*
 * Helper routine to determine whether one set of page permissions (the lhs) is
 * a subset of another one (the rhs).
 */
V8_BASE_EXPORT bool IsSubset(PagePermissions lhs, PagePermissions rhs);

/*
 * The virtual address space of the current process. Conceptionally, there
 * should only be one such "root" instance. However, in practice there is no
 * issue with having multiple instances as the actual resources are managed by
 * the OS kernel.
 */
class V8_BASE_EXPORT VirtualAddressSpace : public VirtualAddressSpaceBase {
 public:
  VirtualAddressSpace();
  ~VirtualAddressSpace() override = default;

  void SetRandomSeed(int64_t seed) override;

  Address RandomPageAddress() override;

  Address AllocatePages(Address hint, size_t size, size_t alignment,
                        PagePermissions access) override;

  void FreePages(Address address, size_t size) override;

  bool SetPagePermissions(Address address, size_t size,
                          PagePermissions access) override;

  bool AllocateGuardRegion(Address address, size_t size) override;

  void FreeGuardRegion(Address address, size_t size) override;

  Address AllocateSharedPages(Address hint, size_t size,
                              PagePermissions permissions,
                              PlatformSharedMemoryHandle handle,
                              uint64_t offset) override;

  void FreeSharedPages(Address address, size_t size) override;

  bool CanAllocateSubspaces() override;

  std::unique_ptr<v8::VirtualAddressSpace> AllocateSubspace(
      Address hint, size_t size, size_t alignment,
      PagePermissions max_page_permissions) override;

  bool RecommitPages(Address address, size_t size,
                     PagePermissions access) override;

  bool DiscardSystemPages(Address address, size_t size) override;

  bool DecommitPages(Address address, size_t size) override;

 private:
  void FreeSubspace(VirtualAddressSubspace* subspace) override;
};

/*
 * A subspace of a parent virtual address space. This represents a reserved
 * contiguous region of virtual address space in the current process.
 */
class V8_BASE_EXPORT VirtualAddressSubspace : public VirtualAddressSpaceBase {
 public:
  ~VirtualAddressSubspace() override;

  void SetRandomSeed(int64_t seed) override;

  Address RandomPageAddress() override;

  Address AllocatePages(Address hint, size_t size, size_t alignment,
                        PagePermissions permissions) override;

  void FreePages(Address address, size_t size) override;

  bool SetPagePermissions(Address address, size_t size,
                          PagePermissions permissions) override;

  bool AllocateGuardRegion(Address address, size_t size) override;

  void FreeGuardRegion(Address address, size_t size) override;

  Address AllocateSharedPages(Address hint, size_t size,
                              PagePermissions permissions,
                              PlatformSharedMemoryHandle handle,
                              uint64_t offset) override;

  void FreeSharedPages(Address address, size_t size) override;

  bool CanAllocateSubspaces() override { return true; }

  std::unique_ptr<v8::VirtualAddressSpace> AllocateSubspace(
      Address hint, size_t size, size_t alignment,
      PagePermissions max_page_permissions) override;

  bool RecommitPages(Address address, size_t size,
                     PagePermissions permissions) override;

  bool DiscardSystemPages(Address address, size_t size) override;

  bool DecommitPages(Address address, size_t size) override;

 private:
  // The VirtualAddressSpace class creates instances of this class when
  // allocating sub spaces.
  friend class v8::base::VirtualAddressSpace;

  void FreeSubspace(VirtualAddressSubspace* subspace) override;

  VirtualAddressSubspace(AddressSpaceReservation reservation,
                         VirtualAddressSpaceBase* parent_space,
                         PagePermissions max_page_permissions);

  // The address space reservation backing this subspace.
  AddressSpaceReservation reservation_;

  // Mutex guarding the non-threadsafe RegionAllocator and
  // RandomNumberGenerator.
  Mutex mutex_;

  // RegionAllocator to manage the virtual address reservation and divide it
  // into further regions as necessary.
  RegionAllocator region_allocator_;

  // Random number generator for generating random addresses.
  RandomNumberGenerator rng_;

  // Pointer to the parent space. Must be kept alive by the owner of this
  // instance during its lifetime.
  VirtualAddressSpaceBase* parent_space_;
};

}  // namespace base
}  // namespace v8
#endif  // V8_BASE_VIRTUAL_ADDRESS_SPACE_H_
