// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "courgette/memory_allocator.h"

#include <stddef.h>
#include <stdint.h>

#include <map>

#include "base/files/file_util.h"
#include "build/build_config.h"

#if BUILDFLAG(IS_WIN)

#include <windows.h>

namespace {

// The file is created in the %TEMP% folder.
// NOTE: Since the file will be used as backing for a memory allocation,
// it will never be so big that size_t cannot represent its size.
base::File CreateTempFile() {
  base::FilePath path;
  if (!base::CreateTemporaryFile(&path))
    return base::File();

  int flags = base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_READ |
              base::File::FLAG_WRITE | base::File::FLAG_DELETE_ON_CLOSE |
              base::File::FLAG_WIN_TEMPORARY;
  return base::File(path, flags);
}

}  // namespace

namespace courgette {

// FileMapping

FileMapping::FileMapping() : mapping_(nullptr), view_(nullptr) {}

FileMapping::~FileMapping() {
  Close();
}

bool FileMapping::InitializeView(size_t size) {
  DCHECK(view_ == nullptr);
  DCHECK(mapping_ != nullptr);
  view_ = ::MapViewOfFile(mapping_, FILE_MAP_WRITE, 0, 0, size);
  if (!view_) {
    Close();
    return false;
  }
  return true;
}

bool FileMapping::Create(HANDLE file, size_t size) {
  DCHECK(file != INVALID_HANDLE_VALUE);
  DCHECK(!valid());
  mapping_ = ::CreateFileMapping(file, nullptr, PAGE_READWRITE, 0, 0, nullptr);
  if (!mapping_)
    return false;

  return InitializeView(size);
}

void FileMapping::Close() {
  if (view_)
    ::UnmapViewOfFile(view_);
  if (mapping_)
    ::CloseHandle(mapping_);
  mapping_ = nullptr;
  view_ = nullptr;
}

bool FileMapping::valid() const {
  return view_ != nullptr;
}

void* FileMapping::view() const {
  return view_;
}

// TempMapping

TempMapping::TempMapping() {
}

TempMapping::~TempMapping() {
}

bool TempMapping::Initialize(size_t size) {
  file_ = CreateTempFile();
  if (!file_.IsValid())
    return false;

  // TODO(tommi): The assumption here is that the alignment of pointers (this)
  // is as strict or stricter than the alignment of the element type.  This is
  // not always true, e.g. __m128 has 16-byte alignment.
  size += sizeof(this);
  if (!file_.SetLength(size) ||
      !mapping_.Create(file_.GetPlatformFile(), size)) {
    file_.Close();
    return false;
  }

  TempMapping** write = reinterpret_cast<TempMapping**>(mapping_.view());
  write[0] = this;

  return true;
}

void* TempMapping::memory() const {
  uint8_t* mem = reinterpret_cast<uint8_t*>(mapping_.view());
  // The 'this' pointer is written at the start of mapping_.view(), so
  // go past it. (See Initialize()).
  if (mem)
    mem += sizeof(this);
  DCHECK(mem);
  return mem;
}

bool TempMapping::valid() const {
  return mapping_.valid();
}

// static
TempMapping* TempMapping::GetMappingFromPtr(void* mem) {
  TempMapping* ret = nullptr;
  if (mem) {
    ret = reinterpret_cast<TempMapping**>(mem)[-1];
  }
  DCHECK(ret);
  return ret;
}

}  // namespace courgette

#endif  // BUILDFLAG(IS_WIN)