/*
 * Copyright (C) 2016 by Argonne National Laboratory.
 * Copyright (C) 2021 Cornelis Networks.
 *
 * This software is available to you under a choice of one of two
 * licenses.  You may choose to be licensed under the terms of the GNU
 * General Public License (GPL) Version 2, available from the file
 * COPYING in the main directory of this source tree, or the
 * BSD license below:
 *
 *     Redistribution and use in source and binary forms, with or
 *     without modification, are permitted provided that the following
 *     conditions are met:
 *
 *      - Redistributions of source code must retain the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer.
 *
 *      - Redistributions in binary form must reproduce the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer in the documentation and/or other materials
 *        provided with the distribution.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
#ifndef _FI_PROV_OPX_ATOMIC_H_
#define _FI_PROV_OPX_ATOMIC_H_

#include "rdma/opx/fi_opx.h"

#include "rdma/opx/fi_opx_compiler.h"
#include "rdma/opx/fi_opx_hfi1_packet.h"
#include "rdma/opx/fi_opx_reliability.h"
/* Macro indirection in order to support other macros as arguments
 * C requires another indirection for expanding macros since
 * operands of the token pasting operator are not expanded */

#define FI_OPX_ATOMIC_SPECIALIZED_FUNC(LOCK, AV, CAPS, RELIABILITY)                                \
	FI_OPX_ATOMIC_SPECIALIZED_FUNC_(LOCK, AV, CAPS, RELIABILITY)

#define FI_OPX_ATOMIC_SPECIALIZED_FUNC_(LOCK, AV, CAPS, RELIABILITY)                               \
	static inline ssize_t fi_opx_atomic_##LOCK##_##AV##_##CAPS##_##RELIABILITY(                \
		struct fid_ep *ep, const void *buf, size_t count, void *desc, fi_addr_t dst_addr,  \
		uint64_t addr, uint64_t key, enum fi_datatype datatype, enum fi_op op,             \
		void *context)                                                                     \
	{                                                                                          \
		return fi_opx_atomic_generic(ep, buf, count, dst_addr, addr, key, datatype, op,    \
					     context, LOCK, AV, CAPS, RELIABILITY);                \
	}                                                                                          \
	static inline ssize_t fi_opx_inject_atomic_##LOCK##_##AV##_##CAPS##_##RELIABILITY(         \
		struct fid_ep *ep, const void *buf, size_t count, fi_addr_t dst_addr,              \
		uint64_t addr, uint64_t key, enum fi_datatype datatype, enum fi_op op)             \
	{                                                                                          \
		return fi_opx_inject_atomic_generic(ep, buf, count, dst_addr, addr, key, datatype, \
						    op, LOCK, AV, CAPS, RELIABILITY);              \
	}                                                                                          \
	static inline ssize_t fi_opx_fetch_atomic_##LOCK##_##AV##_##CAPS##_##RELIABILITY(          \
		struct fid_ep *ep, const void *buf, size_t count, void *desc, void *result,        \
		void *result_desc, fi_addr_t dest_addr, uint64_t addr, uint64_t key,               \
		enum fi_datatype datatype, enum fi_op op, void *context)                           \
	{                                                                                          \
		return fi_opx_fetch_atomic_generic(ep, buf, count, desc, result, result_desc,      \
						   dest_addr, addr, key, datatype, op, context,    \
						   LOCK, AV, CAPS, RELIABILITY);                   \
	}                                                                                          \
	static inline ssize_t fi_opx_compare_atomic_##LOCK##_##AV##_##CAPS##_##RELIABILITY(        \
		struct fid_ep *ep, const void *buf, size_t count, void *desc, const void *compare, \
		void *compare_desc, void *result, void *result_desc, fi_addr_t dest_addr,          \
		uint64_t addr, uint64_t key, enum fi_datatype datatype, enum fi_op op,             \
		void *context)                                                                     \
	{                                                                                          \
		return fi_opx_compare_atomic_generic(ep, buf, count, desc, compare, compare_desc,  \
						     result, result_desc, dest_addr, addr, key,    \
						     datatype, op, context, LOCK, AV, CAPS,        \
						     RELIABILITY);                                 \
	}

#define FI_OPX_ATOMIC_SPECIALIZED_FUNC_NAME(TYPE, LOCK, AV, CAPS, RELIABILITY)                     \
	FI_OPX_ATOMIC_SPECIALIZED_FUNC_NAME_(TYPE, LOCK, AV, CAPS, RELIABILITY)

#define FI_OPX_ATOMIC_SPECIALIZED_FUNC_NAME_(TYPE, LOCK, AV, CAPS, RELIABILITY)                    \
	fi_opx_##TYPE##_##LOCK##_##AV##_##CAPS##_##RELIABILITY

#ifdef __cplusplus
extern "C" {
#endif

#include <complex.h>

static inline size_t sizeofdt(const enum fi_datatype datatype)
{
	static const size_t sizeofdt[FI_DATATYPE_LAST] = {
		sizeof(int8_t), /* FI_INT8 */
		sizeof(uint8_t), /* FI_UINT8 */
		sizeof(int16_t), /* FI_INT16 */
		sizeof(uint16_t), /* FI_UINT16 */
		sizeof(int32_t), /* FI_INT32 */
		sizeof(uint32_t), /* FI_UINT32 */
		sizeof(int64_t), /* FI_INT64 */
		sizeof(uint64_t), /* FI_UINT64 */
		sizeof(float), /* FI_FLOAT */
		sizeof(double), /* FI_DOUBLE */
		sizeof(complex float), /* FI_FLOAT_COMPLEX */
		sizeof(complex double), /* FI_DOUBLE_COMPLEX */
		sizeof(long double), /* FI_LONG_DOUBLE */
		sizeof(complex long double) /* FI_LONG_DOUBLE_COMPLEX */
	};

	return sizeofdt[datatype];
}

static inline size_t maxcount(const enum fi_datatype datatype, const unsigned is_compare,
			      const unsigned is_fetch)
{
#define INIT_MAXCOUNT_ARRAY(maxbytes)                                                              \
	maxbytes / sizeof(int8_t), /* FI_INT8 */                                                   \
		maxbytes / sizeof(uint8_t), /* FI_UINT8 */                                         \
		maxbytes / sizeof(int16_t), /* FI_INT16 */                                         \
		maxbytes / sizeof(uint16_t), /* FI_UINT16 */                                       \
		maxbytes / sizeof(int32_t), /* FI_INT32 */                                         \
		maxbytes / sizeof(uint32_t), /* FI_UINT32 */                                       \
		maxbytes / sizeof(int64_t), /* FI_INT64 */                                         \
		maxbytes / sizeof(uint64_t), /* FI_UINT64 */                                       \
		maxbytes / sizeof(float), /* FI_FLOAT */                                           \
		maxbytes / sizeof(double), /* FI_DOUBLE */                                         \
		maxbytes / sizeof(complex float), /* FI_FLOAT_COMPLEX */                           \
		maxbytes / sizeof(complex double), /* FI_DOUBLE_COMPLEX */                         \
		maxbytes / sizeof(long double), /* FI_LONG_DOUBLE */                               \
		maxbytes / sizeof(complex long double) /* FI_LONG_DOUBLE_COMPLEX */

	static const size_t maxcount[2][2][FI_DATATYPE_LAST] = {
		{ { /* !compare, !fetch */
		    INIT_MAXCOUNT_ARRAY(FI_OPX_HFI1_PACKET_MTU) },
		  { /* !compare, fetch */
		    INIT_MAXCOUNT_ARRAY((FI_OPX_HFI1_PACKET_MTU -
					 sizeof(struct fi_opx_hfi1_fetch_metadata))) } },
		{ { /* compare, !fetch */
		    INIT_MAXCOUNT_ARRAY(FI_OPX_HFI1_PACKET_MTU >> 1) },
		  { /* compare, fetch */
		    INIT_MAXCOUNT_ARRAY(((FI_OPX_HFI1_PACKET_MTU >> 1) -
					 sizeof(struct fi_opx_hfi1_fetch_metadata))) } }
	};

#undef INIT_MAXCOUNT_ARRAY

	return maxcount[is_compare][is_fetch][datatype];
}

ssize_t fi_opx_fetch_atomic_generic(struct fid_ep *ep, const void *buf, size_t count, void *desc,
				    void *result, void *result_desc, fi_addr_t dest_addr,
				    uint64_t addr, uint64_t key, enum fi_datatype datatype,
				    enum fi_op op, void *context, const int lock_required,
				    const enum fi_av_type av_type, const uint64_t caps,
				    const enum ofi_reliability_kind reliability);

ssize_t fi_opx_compare_atomic_generic(struct fid_ep *ep, const void *buf, size_t count, void *desc,
				      const void *compare, void *compare_desc, void *result,
				      void *result_desc, fi_addr_t dest_addr, uint64_t addr,
				      uint64_t key, enum fi_datatype datatype, enum fi_op op,
				      void *context, int lock_required,
				      const enum fi_av_type av_type, const uint64_t caps,
				      const enum ofi_reliability_kind reliability);

ssize_t fi_opx_inject_atomic_generic(struct fid_ep *ep, const void *buf, size_t count,
				     fi_addr_t dest_addr, uint64_t addr, uint64_t key,
				     enum fi_datatype datatype, enum fi_op op, int lock_required,
				     const enum fi_av_type av_type, const uint64_t caps,
				     const enum ofi_reliability_kind reliability);

#ifdef __cplusplus
}
#endif

#endif /* _FI_PROV_OPX_ATOMIC_H_ */
