// Copyright 2018 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/base/virtual-address-space-page-allocator.h"

namespace v8 {
namespace base {

VirtualAddressSpacePageAllocator::VirtualAddressSpacePageAllocator(
    v8::VirtualAddressSpace* vas)
    : vas_(vas) {}

void* VirtualAddressSpacePageAllocator::AllocatePages(
    void* hint, size_t size, size_t alignment,
    PageAllocator::Permission access) {
  return reinterpret_cast<void*>(
      vas_->AllocatePages(reinterpret_cast<Address>(hint), size, alignment,
                          static_cast<PagePermissions>(access)));
}

bool VirtualAddressSpacePageAllocator::FreePages(void* ptr, size_t size) {
  MutexGuard guard(&mutex_);
  Address address = reinterpret_cast<Address>(ptr);
  // Was this allocation resized previously? If so, use the original size.
  auto result = resized_allocations_.find(address);
  if (result != resized_allocations_.end()) {
    size = result->second;
    resized_allocations_.erase(result);
  }
  vas_->FreePages(address, size);
  return true;
}

bool VirtualAddressSpacePageAllocator::ReleasePages(void* ptr, size_t size,
                                                    size_t new_size) {
  // The VirtualAddressSpace class doesn't support this method because it can't
  // be properly implemented on top of Windows placeholder mappings (they cannot
  // be partially freed or resized while being allocated). Instead, we emulate
  // this behaviour by decommitting the released pages, which in effect achieves
  // exactly what ReleasePages would normally do as well. However, we still need
  // to pass the original size to FreePages eventually, so we'll need to keep
  // track of that.
  DCHECK_LE(new_size, size);

  MutexGuard guard(&mutex_);
  // Will fail if the allocation was resized previously, which is desired.
  Address address = reinterpret_cast<Address>(ptr);
  resized_allocations_.insert({address, size});
  CHECK(vas_->DecommitPages(address + new_size, size - new_size));
  return true;
}

bool VirtualAddressSpacePageAllocator::SetPermissions(
    void* address, size_t size, PageAllocator::Permission access) {
  return vas_->SetPagePermissions(reinterpret_cast<Address>(address), size,
                                  static_cast<PagePermissions>(access));
}

bool VirtualAddressSpacePageAllocator::RecommitPages(
    void* address, size_t size, PageAllocator::Permission access) {
  return vas_->RecommitPages(reinterpret_cast<Address>(address), size,
                             static_cast<PagePermissions>(access));
}

bool VirtualAddressSpacePageAllocator::DiscardSystemPages(void* address,
                                                          size_t size) {
  return vas_->DiscardSystemPages(reinterpret_cast<Address>(address), size);
}

bool VirtualAddressSpacePageAllocator::DecommitPages(void* address,
                                                     size_t size) {
  return vas_->DecommitPages(reinterpret_cast<Address>(address), size);
}

}  // namespace base
}  // namespace v8
