
/*
 *         PVM version 3.3:  Parallel Virtual Machine System
 *               University of Tennessee, Knoxville TN.
 *           Oak Ridge National Laboratory, Oak Ridge TN.
 *                   Emory University, Atlanta GA.
 *      Authors:  A. L. Beguelin, J. J. Dongarra, G. A. Geist,
 *    W. C. Jiang, R. J. Manchek, B. K. Moore, and V. S. Sunderam
 *                   (C) 1992 All Rights Reserved
 *
 *                              NOTICE
 *
 * Permission to use, copy, modify, and distribute this software and
 * its documentation for any purpose and without fee is hereby granted
 * provided that the above copyright notice appear in all copies and
 * that both the copyright notice and this permission notice appear in
 * supporting documentation.
 *
 * Neither the Institutions (Emory University, Oak Ridge National
 * Laboratory, and University of Tennessee) nor the Authors make any
 * representations about the suitability of this software for any
 * purpose.  This software is provided ``as is'' without express or
 * implied warranty.
 *
 * PVM version 3 was funded in part by the U.S. Department of Energy,
 * the National Science Foundation and the State of Tennessee.
 */

/*
 *	mesg.c
 *
 *	Pvmd message descriptors.
 *
$Log: mesg.c,v $
 * Revision 1.2  1994/06/03  20:38:18  manchek
 * version 3.3.0
 *
 * Revision 1.1  1993/08/30  23:26:49  manchek
 * Initial revision
 *
 */

#include "global.h"
#include "protoglarp.h"
#include "pvmalloc.h"
#include "tdpro.h"
#include "ddpro.h"
#include "pvmfrag.h"
#include "mesg.h"
#include "listmac.h"
#include "bfunc.h"


extern void pvmbailout();

extern int mytid;					/* from pvmd.c */
extern int ourudpmtu;				/* from pvmd.c */


/***************
 **  Private  **
 **           **
 ***************/

static char rcsid[] = "$Id: mesg.c,v 1.2 1994/06/03 20:38:18 manchek Exp $";


/*****************
 **  Interface  **
 **             **
 *****************/

struct mesg *
mesg_new(master)
	int master;		/* true if a master (no data) node */
{
	struct mesg *mp;

	if (!(mp = TALLOC(1, struct mesg, "mesg"))) {
		pvmlogerror("mesg_new() can't get memory\n");
		pvmbailout(0);
	}
	mp->m_ref = 1;
	if (master) {
		mp->m_link = mp->m_rlink = mp;
		mp->m_frag = 0;
		mp->m_enc = 0;
	} else {
		mp->m_link = mp->m_rlink = 0;
		mp->m_frag = fr_new(0);
		mp->m_enc = 1;
	}
	mp->m_src = mytid;
	mp->m_len = mp->m_dst = mp->m_cod = mp->m_wid = mp->m_flag = mp->m_cpos = 0;
	mp->m_cfrag = 0;
	return mp;
}


/*	mesg_free()
*
*	Free a message or list of messages.  If m_frag is nonzero, this is
*	a slave node; unlink and free it and its frag list.  Else, it's
*	a master node; free all slave nodes too.
*/

void
mesg_free(mp)
	struct mesg *mp;
{
	if (mp->m_frag) {	/* slave mesg */
		if (mp->m_link && mp->m_rlink) {
			LISTDELETE(mp, m_link, m_rlink);
		}
		fr_unref(mp->m_frag);

	} else {	/* master */
		while (mp->m_link != mp)
			mesg_free(mp->m_link);
	}
	PVM_FREE(mp);
}


/*	mesg_unref()
*
*/

void
mesg_unref(mp)
	struct mesg *mp;
{
	if (--mp->m_ref < 1)
		mesg_free(mp);
}


struct mca *
mca_new()
{
	struct mca *mcap;

	if (mcap = TALLOC(1, struct mca, "mca")) {
		mcap->mc_link = mcap->mc_rlink = mcap;
		mcap->mc_tid = mcap->mc_ndst = 0;
		mcap->mc_dsts = 0;
	}
	return mcap;
}


void
mca_free(mcap)
	struct mca *mcap;
{
	LISTDELETE(mcap, mc_link, mc_rlink);
	if (mcap->mc_dsts)
		PVM_FREE(mcap->mc_dsts);
	PVM_FREE(mcap);
}


void
mesg_rewind(mp)
	struct mesg *mp;
{
	mp->m_cfrag = mp->m_frag->fr_link;
	mp->m_cpos = 0;
}


/*	bytepk()
*
*	Append a stream of bytes to msg.  Allocate more fragments as
*	necessary.
*	Returns 0 else 1 if malloc fails.
*/

int
bytepk(mp, cp, num, siz, lnc)
	struct mesg *mp;	/* the message */
	char *cp;			/* base of data */
	int num;			/* num of chunks */
	int siz;			/* size of chunk */
	int lnc;			/* lead to next chunk */
{
	struct frag *fp;			/* working frag */
	int togo;					/* bytes left in chunk */
	int r;						/* bytes (space) left in frag */
	struct frag *nfp;

	if (siz == lnc) {		/* if contiguous, treat as single chunk */
		lnc = (siz *= num);
		num = 1;
	}
	lnc -= siz;		/* now bytes between chunks */

	fp = mp->m_frag->fr_rlink;

	while (num-- > 0) {		/* copy chunks until done */

		for (togo = siz; togo > 0; ) {
			r = fp->fr_max - (fp->fr_dat - fp->fr_buf) - fp->fr_len;

			if (togo <= r) {	/* space in frag for entire chunk */
				BCOPY(cp, fp->fr_dat + fp->fr_len, togo);
				fp->fr_len += togo;
				mp->m_len += togo;
				cp += togo;
				togo = 0;

			} else {
				if (r > 0) {	/* space for part of chunk */
					BCOPY(cp, fp->fr_dat + fp->fr_len, r);
					fp->fr_len += r;
					mp->m_len += r;
					togo -= r;
					cp += r;

				} else {		/* no space, add new frag */
					if (!(nfp = fr_new(ourudpmtu)))
						return 1;
					nfp->fr_dat += MAXHDR;
					LISTPUTAFTER(fp, nfp, fr_link, fr_rlink);
					fp = nfp;
				}
			}
		}
		cp += lnc;
	}
	return 0;
}


/*	byteupk()
*
*	Extract bytes from msg.
*	Returns 0 else 1 if end of message.
*/

int
byteupk(mp, cp, num, siz, lnc)
	struct mesg *mp;	/* the message */
	char *cp;			/* base of data */
	int num;			/* num of chunks */
	int siz;			/* size of chunk */
	int lnc;			/* lead to next chunk */
{
	struct frag *fp;			/* working frag */
	int togo;					/* bytes left in chunk */
	int r;						/* bytes (data) left in frag */

	if (mp->m_cfrag == mp->m_frag)
		return 1;

	if (siz == lnc) {		/* if contiguous, treat as single chunk */
		lnc = (siz *= num);
		num = 1;
	}
	lnc -= siz;		/* now bytes between chunks */

	while (num-- > 0) {		/* copy chunks until done */

		for (togo = siz; togo > 0; ) {
			fp = mp->m_cfrag;
			r = fp->fr_len - mp->m_cpos;

			if (togo <= r) {	/* frag contains rest of chunk */
				BCOPY(fp->fr_dat + mp->m_cpos, cp, togo);
				mp->m_cpos += togo;
				cp += togo;
				togo = 0;

			} else {
				if (r > 0) {	/* frag contains part of chunk */
					BCOPY(fp->fr_dat + mp->m_cpos, cp, r);
					mp->m_cpos += r;
					togo -= r;
					cp += r;

				} else {		/* no space, add new frag */
					mp->m_cpos = 0;
					if ((mp->m_cfrag = fp->fr_link) == mp->m_frag)
						return 1;
				}
			}
		}
		cp += lnc;
	}
	return 0;
}


/*	pkint()
*
*	Pack a 32-bit int into a message.
*	Returns 0 if ok, else 1 if malloc fails.
*/

int
pkint(mp, i)
	struct mesg *mp;		/* message to pack */
	int i;					/* int to pack */
{
	char buf[4];

	buf[0] = i >> 24;
	buf[1] = i >> 16;
	buf[2] = i >> 8;
	buf[3] = i;
	return bytepk(mp, buf, 4, 1, 1);
}


/*	upkint()
*
*	Unpack a signed 32-bit int from a message.
*	Returns 0 if ok, else 1 if end of message.
*/

int
upkint(mp, np)
	struct mesg *mp;		/* message to unpack */
	int *np;				/* int to unpack into */
{
	int cc;
	char buf[4];

	if (cc = byteupk(mp, buf, 4, 1, 1))
		return cc;
	*np = (0x80 & buf[0] ? ~0xffffffff : 0)
	+ ((0xff & buf[0]) << 24)
	+ ((0xff & buf[1]) << 16)
	+ ((0xff & buf[2]) << 8)
	+ (0xff & buf[3]);
	return 0;
}


/*	upkuint()
*
*	Unpack an unsigned 32-bit int from a message.
*	Returns 0 if ok, else 1 if end of message.
*/

int
upkuint(mp, np)
	struct mesg *mp;		/* message to unpack */
	int *np;				/* int to unpack into */
{
	int cc;
	char buf[4];

	if (cc = byteupk(mp, buf, 4, 1, 1))
		return cc;
	*np = ((0xff & buf[0]) << 24)
	+ ((0xff & buf[1]) << 16)
	+ ((0xff & buf[2]) << 8)
	+ (0xff & buf[3]);
	return 0;
}


/*	pkstr()
*
*	Pack a null-term string into a message.
*	Returns 0 if ok, else 1 if malloc fails.
*/

int
pkstr(mp, s)
	struct mesg *mp;		/* message to pack */
	char *s;				/* string to pack */
{
	int cc;
	int l = strlen(s) + 1;

	if (cc = pkint(mp, l))
		return cc;
	return bytepk(mp, s, l, 1, 1);
}


/*	upkstr()
*
*	Unpack a string from a message.  Result is null-terminated.
*	Any length greater than mlen is discarded from message..
*	Returns 0 if ok, else 1 if end of message.
*/

int
upkstr(mp, s, mlen)
	struct mesg *mp;		/* message to unpack */
	char *s;				/* space to unpack in */
	int mlen;				/* max bytes to unpack incl null */
{
	int cc;
	int l;

	if (cc = upkint(mp, &l))
		return cc;
	if (l < mlen)
		return byteupk(mp, s, l, 1, 1);
	if (cc = byteupk(mp, s, mlen, 1, 1))
		return cc;
	s[mlen - 1] = 0;
	while (mlen < l && !byteupk(mp, (char*)&cc, 1, 1, 1));
	return 0;
}


/*	upkstralloc()
*
*	Unpack a string from a message.  Result is null-terminated,
*	and in dynamic space..
*	Returns 0 if ok, else 1 if end of message.
*/

int
upkstralloc(mp, ss)
	struct mesg *mp;		/* message to unpack */
	char **ss;				/* return pointer */
{
	int cc;
	int l;

	if (cc = upkint(mp, &l))
		goto fail;
	*ss = TALLOC(l, char, "ustr");
	if (!(cc = byteupk(mp, *ss, l, 1, 1)))
		return cc;
	PVM_FREE(*ss);

fail:
	*ss = 0;
	return cc;
}


