/*
 * Copyright (C) by Argonne National Laboratory
 *     See COPYRIGHT in top-level directory
 */

#ifndef MPIR_POINTERS_H_INCLUDED
#define MPIR_POINTERS_H_INCLUDED

#include "mpi.h"
#include "mpichconf.h"
#include "mpichconfconst.h"
#include "mpir_assert.h"
#include "mpl.h"


/* This test is lame.  Should eventually include cookie test
   and in-range addresses */
#define MPIR_Valid_ptr_class(kind,ptr,errclass,err) \
    do {                                                                \
        if (!(ptr)) {                                                   \
            err = MPIR_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, __func__, __LINE__, errclass, \
                                       "**nullptrtype", "**nullptrtype %s", #kind); \
            /* Explicitly tell Coverity that errclass != MPI_SUCCESS => err != MPI_SUCCESS */ \
            MPIR_Assert((errclass) == MPI_SUCCESS || ((err) != MPI_SUCCESS)); \
        }                                                               \
    } while (0)

#define MPIR_Info_valid_ptr(ptr,err) MPIR_Valid_ptr_class(Info,ptr,MPI_ERR_INFO,err)
/* Check not only for a null pointer but for an invalid communicator,
   such as one that has been freed.  Let's try the ref_count as the test
   for now */
/* ticket #1441: check (refcount<=0) to cover the case of 0, an "over-free" of
 * -1 or similar, and the 0xecec... case when --enable-g=mem is used */
#define MPIR_Comm_valid_ptr(ptr,err,ignore_rev) {     \
     MPIR_Valid_ptr_class(Comm,ptr,MPI_ERR_COMM,err); \
     if ((ptr) && MPIR_Object_get_ref(ptr) <= 0) {    \
         MPIR_ERR_SET(err,MPI_ERR_COMM,"**comm");     \
         ptr = 0;                                     \
     } else if ((ptr) && (ptr)->revoked && !(ignore_rev)) {        \
         MPIR_ERR_SET(err,MPIX_ERR_REVOKED,"**comm"); \
     }                                                \
}
#define MPIR_Win_valid_ptr(ptr,err) MPIR_Valid_ptr_class(Win,ptr,MPI_ERR_WIN,err)
#define MPIR_Group_valid_ptr(ptr,err) MPIR_Valid_ptr_class(Group,ptr,MPI_ERR_GROUP,err)
#define MPIR_Op_valid_ptr(ptr,err) MPIR_Valid_ptr_class(Op,ptr,MPI_ERR_OP,err)
#define MPIR_Errhandler_valid_ptr(ptr,err) MPIR_Valid_ptr_class(Errhandler,ptr,MPI_ERR_ARG,err)
#define MPIR_Request_valid_ptr(ptr,err) MPIR_Valid_ptr_class(Request,ptr,MPI_ERR_REQUEST,err)
#define MPII_Keyval_valid_ptr(ptr,err) MPIR_Valid_ptr_class(Keyval,ptr,MPI_ERR_KEYVAL,err)
#define MPIR_Session_valid_ptr(ptr,err) MPIR_Valid_ptr_class(Session,ptr,MPI_ERR_SESSION,err)


/* Assigns (src_) to (dst_), checking that (src_) fits in (dst_) without
 * truncation.
 *
 * When fiddling with this macro, please keep C's overly complicated integer
 * promotion/truncation/conversion rules in mind.  A discussion of these issues
 * can be found in Chapter 5 of "Secure Coding in C and C++" by Robert Seacord.
 */
#define MPIR_Assign_trunc(dst_,src_,dst_type_)                                         \
    do {                                                                               \
        /* will catch some of the cases if the expr_inttype macros aren't available */ \
        MPIR_Assert((src_) == (dst_type_)(src_));                                      \
        dst_ = (dst_type_)(src_);                                                      \
    } while (0)

/*
 * Ensure an MPI_Aint value fits into a signed int.
 * Useful for detecting overflow when MPI_Aint is larger than an int.
 *
 * \param[in]  aint  Variable of type MPI_Aint
 */
#define MPIR_Ensure_Aint_fits_in_int(aint) \
  MPIR_Assert((aint) == (MPI_Aint)(int)(aint));

#endif /* MPIR_POINTERS_H_INCLUDED */
