/*
 * Copyright (C) 2006 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef ANDROID_USB_NEW_DELETE_H__
#define ANDROID_USB_NEW_DELETE_H__
/** \file
  This file consists implementations of our 'new' and 'delete' operators
*/

#include "android_usb_pool_tags.h"

/** \brief Checks if given pool type is one of NonPaged pool kinds.

  All numeric values for all NonPaged pool types are even numbers while all
  numeric values for all PagedPool types are odd numbers (see definition of
  POOL_TYPE enum). So this routine utilizes this to see whether given pool
  type is one of NonPaged pool kinds. This routine can be called at any IRQL.
  @param pool_type[in] Pool type
  @return True if pool type is one of NonPaged pool types, false otherwise
*/
__forceinline bool IsPoolNonPaged(POOL_TYPE pool_type) {
  return (0 == (pool_type & 0x1));
}

/** @name Operators new and delete
 
  In Kernel Mode development each memory allocation must specify type of the
  pool from which memory should be allocated, usualy PagedPool or NonPagedPool.
  Because of that "traditional" operator 'new' that takes only one parameter
  (memory size) is not good so we modify that operator by adding two more
  parameters: pool type and memory tag (last one is optional but highly
  encouraged). To prevent from mistakes, traditional operator 'new' is also
  defined. It will allocate requested number of bytes from NonPagedPool with
  default memory tag but it will always assert on checked (debug) builds.
  Since there is no infrastructure for C++ exceptions in Kernel Mode we are
  not using them to report memory allocation error. So, on failure operators
  'new' are returning NULL instead of throwing an exception.
*/
///@{

/** \brief Main operator new

  This is the main operator new that allocates specified number of bytes from
  the specified pool and assigns a custom tag to the allocated memory.
  Inherits IRQL restrictions for ExAllocatePoolWithTag (see the DDK doc).
  @param size[in] Number of bytes to allocate.
  @param pool_type[in] Type of the pool to allocate from.
  @param pool_tag[in] A tag to attach to the allocated memory. Since utilities
         that display tags use their ASCII representations it's advisable to
         use tag values that are ASCII symbols, f.i. 'ATag'. Note that due to
         inversion of bytes in stored ULONG value, to read 'ATag' in the tag
         displaying utility, the actual value passed to operator 'new' must be
         'gaTA'
  @return Pointer to allocated memory on success, NULL on error.
*/
__forceinline void* __cdecl operator new(size_t size,
                                         POOL_TYPE pool_type,
                                         ULONG pool_tag) {
  ASSERT((pool_type < MaxPoolType) && (0 != size));
  // Enforce IRQL restriction check.
  ASSERT(IsPoolNonPaged(pool_type) || (KeGetCurrentIrql() < DISPATCH_LEVEL));
  return size ? ExAllocatePoolWithTag(pool_type,
                                      static_cast<ULONG>(size),
                                      pool_tag) :
                NULL;
}

/** \brief
  Short operator new that attaches a default tag to the allocated memory.

  This version of operator new allocates specified number of bytes from the
  specified pool and assigns a default tag (GANDR_POOL_TAG_DEFAULT) to the
  allocated memory. Inherits IRQL restrictions for ExAllocatePoolWithTag.
  @param size[in] Number of bytes to allocate.
  @param pool_type[in] Type of the pool to allocate from.
  @return Pointer to allocated memory on success, NULL on error.
*/
__forceinline void* __cdecl operator new(size_t size, POOL_TYPE pool_type) {
  ASSERT((pool_type < MaxPoolType) && (0 != size));
  // Enforce IRQL restriction check.
  ASSERT(IsPoolNonPaged(pool_type) || (KeGetCurrentIrql() < DISPATCH_LEVEL));
  return size ? ExAllocatePoolWithTag(pool_type,
                                      static_cast<ULONG>(size),
                                      GANDR_POOL_TAG_DEFAULT) :
                NULL;
}

/** \brief Traditional operator new that should never be used.

  Using of this version of operator 'new' is prohibited in Kernel Mode
  development. For the sake of safety it is implemented though to allocate
  requested number of bytes from the NonPagedPool and attach default tag
  to the allocated memory. It will assert on checked (debug) builds.
  Inherits IRQL restrictions for ExAllocatePoolWithTag.
  @param size[in] Number of bytes to allocate.
  @return Pointer to memory allocated from NonPagedPool on success or NULL on
          error.
*/
__forceinline void* __cdecl operator new(size_t size) {
  ASSERTMSG("\n!!! Using of operator new(size_t size) is detected!\n"
    "This is illegal in our driver C++ development environment to use "
    "this version of operator 'new'. Please switch to\n"
    "new(size_t size, POOL_TYPE pool_type) or "
    "new(size_t size, POOL_TYPE pool_type, ULONG pool_tag) ASAP!!!\n",
    false);
  ASSERT(0 != size);
  return size ? ExAllocatePoolWithTag(NonPagedPool,
                                      static_cast<ULONG>(size),
                                      GANDR_POOL_TAG_DEFAULT) :
                NULL;
}

/** \brief Operator delete.

  Frees memory allocated by 'new' operator.
  @param pointer[in] Memory to free. If this parameter is NULL operator does
         nothing but asserts on checked build. Inherits IRQL restrictions
         for ExFreePool.
*/
__forceinline void __cdecl operator delete(void* pointer) {
  ASSERT(NULL != pointer);
  if (NULL != pointer)
    ExFreePool(pointer);
}

/** \brief Operator delete for arrays.

  Frees memory allocated by 'new' operator.
  @param pointer[in] Memory to free. If this parameter is NULL operator does
         nothing but asserts on checked build. Inherits IRQL restrictions
         for ExFreePool.
*/
__forceinline void __cdecl operator delete[](void* pointer) {
  ASSERT(NULL != pointer);
  if (NULL != pointer)
    ExFreePool(pointer);
}

///@}

#endif  // ANDROID_USB_NEW_DELETE_H__
