/* Copyright (C) 2016 The Android Open Source Project
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This file implements interfaces from the file jvmti.h. This implementation
 * is licensed under the same terms as the file jvmti.h.  The
 * copyright and license information for the file jvmti.h follows.
 *
 * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

#ifndef ART_OPENJDKJVMTI_JVMTI_ALLOCATOR_H_
#define ART_OPENJDKJVMTI_JVMTI_ALLOCATOR_H_

#include <android-base/logging.h>
#include <android-base/macros.h>

#include "jvmti.h"

#include "ti_allocator.h"

namespace openjdkjvmti {

template <typename T> class JvmtiAllocator;

template <>
class JvmtiAllocator<void> {
 public:
  typedef void value_type;
  typedef void* pointer;
  typedef const void* const_pointer;

  template <typename U>
  struct rebind {
    typedef JvmtiAllocator<U> other;
  };

  explicit JvmtiAllocator(jvmtiEnv* env) : env_(env) {}
  JvmtiAllocator() : env_(nullptr) {}

  template <typename U>
  JvmtiAllocator(const JvmtiAllocator<U>& other)
      : env_(other.env_) {}

  JvmtiAllocator(const JvmtiAllocator& other) = default;
  JvmtiAllocator& operator=(const JvmtiAllocator& other) = default;
  ~JvmtiAllocator() = default;

 private:
  jvmtiEnv* env_;

  template <typename U>
  friend class JvmtiAllocator;

  template <typename U>
  friend bool operator==(const JvmtiAllocator<U>& lhs, const JvmtiAllocator<U>& rhs);
};

template <typename T>
class JvmtiAllocator {
 public:
  typedef T value_type;
  typedef T* pointer;
  typedef T& reference;
  typedef const T* const_pointer;
  typedef const T& const_reference;
  typedef size_t size_type;
  typedef ptrdiff_t difference_type;

  template <typename U>
  struct rebind {
    typedef JvmtiAllocator<U> other;
  };

  explicit JvmtiAllocator(jvmtiEnv* env) : env_(env) {}
  JvmtiAllocator() : env_(nullptr) {}

  template <typename U>
  JvmtiAllocator(const JvmtiAllocator<U>& other)
      : env_(other.env_) {}

  JvmtiAllocator(const JvmtiAllocator& other) = default;
  JvmtiAllocator& operator=(const JvmtiAllocator& other) = default;
  ~JvmtiAllocator() = default;

  size_type max_size() const {
    return static_cast<size_type>(-1) / sizeof(T);
  }

  pointer address(reference x) const { return &x; }
  const_pointer address(const_reference x) const { return &x; }

  pointer allocate(size_type n, JvmtiAllocator<void>::pointer hint ATTRIBUTE_UNUSED = nullptr) {
    DCHECK_LE(n, max_size());
    if (env_ == nullptr) {
      T* result = reinterpret_cast<T*>(AllocUtil::AllocateImpl(n * sizeof(T)));
      CHECK(result != nullptr || n == 0u);  // Abort if AllocateImpl() fails.
      return result;
    } else {
      unsigned char* result;
      jvmtiError alloc_error = env_->Allocate(n * sizeof(T), &result);
      CHECK(alloc_error == JVMTI_ERROR_NONE);
      return reinterpret_cast<T*>(result);
    }
  }
  void deallocate(pointer p, size_type n ATTRIBUTE_UNUSED) {
    if (env_ == nullptr) {
      AllocUtil::DeallocateImpl(reinterpret_cast<unsigned char*>(p));
    } else {
      jvmtiError dealloc_error = env_->Deallocate(reinterpret_cast<unsigned char*>(p));
      CHECK(dealloc_error == JVMTI_ERROR_NONE);
    }
  }

  void construct(pointer p, const_reference val) {
    new (static_cast<void*>(p)) value_type(val);
  }
  template <class U, class... Args>
  void construct(U* p, Args&&... args) {
    ::new (static_cast<void*>(p)) U(std::forward<Args>(args)...);
  }
  void destroy(pointer p) {
    p->~value_type();
  }

  inline bool operator==(JvmtiAllocator const& other) {
    return env_ == other.env_;
  }
  inline bool operator!=(JvmtiAllocator const& other) {
    return !operator==(other);
  }

 private:
  jvmtiEnv* env_;

  template <typename U>
  friend class JvmtiAllocator;

  template <typename U>
  friend bool operator==(const JvmtiAllocator<U>& lhs, const JvmtiAllocator<U>& rhs);
};

template <typename T>
inline bool operator==(const JvmtiAllocator<T>& lhs, const JvmtiAllocator<T>& rhs) {
  return lhs.env_ == rhs.env_;
}

template <typename T>
inline bool operator!=(const JvmtiAllocator<T>& lhs, const JvmtiAllocator<T>& rhs) {
  return !(lhs == rhs);
}

}  // namespace openjdkjvmti

#endif  // ART_OPENJDKJVMTI_JVMTI_ALLOCATOR_H_
