/* -*- C -*-
 *
 * Copyright (c) 2020      Intel, Inc.  All rights reserved.
 * $COPYRIGHT$
 *
 * Additional copyrights may follow
 *
 * $HEADER$
 *
 */
#ifndef PMIX_PTL_BASE_HANDSHAKE_H_
#define PMIX_PTL_BASE_HANDSHAKE_H_

#include "src/include/pmix_config.h"


#ifdef HAVE_STRING_H
#include <string.h>
#endif


/* The following macros are for use in the PTL connection
 * handler. For simplicity, they include variables that are
 * defined in the functions where they are used - thus, these
 * macros are NOT portable for use elsewhere
 *
 * A symmetric pair of macros are provided specifically to
 * make it easier to read and compare the two ends of the
 * handshake */

BEGIN_C_DECLS

/* The following macros are used in the ptl_base_connection_hdlr.c
 * file to parse the handshake message and extract its fields */

#define PMIX_PTL_GET_STRING(n)                  \
    do {                                        \
        size_t _l;                              \
        PMIX_STRNLEN(_l, mg, cnt);              \
        if (_l < cnt) {                         \
            (n) = strdup(mg);                   \
            mg += strlen((n)) + 1;              \
            cnt -= strlen((n)) + 1;             \
        } else {                                \
            PMIX_ERROR_LOG(PMIX_ERR_BAD_PARAM); \
            goto error;                         \
        }                                       \
    } while(0)

/* numerical value must be converted back to host
 * order, so we use an intermediate storage place
 * for that purpose */
#define PMIX_PTL_GET_U32(n)                     \
    do {                                        \
        uint32_t _u;                            \
        if (sizeof(uint32_t) <= cnt) {          \
            memcpy(&_u, mg, sizeof(uint32_t));  \
            (n) = ntohl(_u);                    \
            mg += sizeof(uint32_t);             \
            cnt -= sizeof(uint32_t);            \
        } else {                                \
            PMIX_ERROR_LOG(PMIX_ERR_BAD_PARAM); \
            goto error;                         \
        }                                       \
    } while(0)

#define PMIX_PTL_GET_BLOB(b, l)                 \
    do {                                        \
        if (0 < (l)) {                          \
            (b) = (char*)malloc((l));           \
            if (NULL == (b)) {                  \
                PMIX_ERROR_LOG(PMIX_ERR_NOMEM); \
                goto error;                     \
            }                                   \
            memcpy((b), mg, (l));               \
            mg += (l);                          \
            cnt -= (l);                         \
        }                                       \
    } while(0)

#define PMIX_PTL_GET_U8(n)                      \
    do {                                        \
        if (sizeof(uint8_t) <= cnt) {           \
            memcpy(&(n), mg, sizeof(uint8_t));  \
            mg += sizeof(uint8_t);              \
            cnt -= sizeof(uint8_t);             \
        } else {                                \
            PMIX_ERROR_LOG(PMIX_ERR_BAD_PARAM); \
            goto error;                         \
        }                                       \
    } while(0)

#define PMIX_PTL_GET_PROCID(p)          \
    do {                                \
        char *n;                        \
        uint32_t r;                     \
        PMIX_PTL_GET_STRING(n);         \
        PMIX_PTL_GET_U32(r);            \
        PMIX_LOAD_PROCID(&(p), n, r);   \
        free(n);                        \
    } while(0)



/* The following macros are for use in the ptl_base_fns.c
 * when constructing the handshake message. */
#define PMIX_PTL_PUT_STRING(n)                  \
    do {                                        \
        memcpy(msg+csize, (n), strlen((n)));    \
        csize += strlen((n))+1;                 \
    } while(0)

/* numerical value must be converted to network byte
 * order, so we use an intermediate storage place
 * for that purpose */
#define PMIX_PTL_PUT_U32(n)                         \
    do {                                            \
        uint32_t _u;                                \
        _u = htonl((uint32_t)(n));                  \
        memcpy(msg+csize, &_u, sizeof(uint32_t));   \
        csize += sizeof(uint32_t);                  \
    } while(0)

#define PMIX_PTL_PUT_BLOB(b, l)             \
    do {                                    \
        if (0 < (l)) {                      \
            memcpy(msg+csize, (b), (l));    \
            csize += (l);                   \
        }                                   \
    } while(0)

#define PMIX_PTL_PUT_U8(n)                          \
    do {                                            \
        memcpy(msg+csize, &(n), sizeof(uint8_t));   \
        csize += sizeof(uint8_t);                   \
    } while(0)

#define PMIX_PTL_PUT_PROCID(p)              \
    do {                                    \
        PMIX_PTL_PUT_STRING((p).nspace);    \
        PMIX_PTL_PUT_U32((p).rank);         \
    } while(0)


/* the following macros are for use in the ptl_base_fns.c
 * when sending/recving values during the handshake */
#define PMIX_PTL_RECV_NSPACE(s, n)                                              \
    do {                                                                        \
        pmix_status_t r;                                                        \
        r = pmix_ptl_base_recv_blocking((s), (char*)(n), PMIX_MAX_NSLEN+1);     \
        if (PMIX_SUCCESS != r) {                                                \
            return r;                                                           \
        }                                                                       \
    } while(0)

#define PMIX_PTL_RECV_U32(s, n)                                                 \
    do {                                                                        \
        pmix_status_t r;                                                        \
        uint32_t _u;                                                            \
        r = pmix_ptl_base_recv_blocking((s), (char*)&_u, sizeof(uint32_t));     \
        if (PMIX_SUCCESS != r) {                                                \
            return r;                                                           \
        }                                                                       \
        (n) = htonl(_u);                                                        \
    } while(0)

END_C_DECLS

#endif
