/*
 * nmimp.c: Methods for IMP data marshaling/unmarshaling.
 *
 * Bruce Nelson, interWAVE, 08/16/95.
 */

#include <stdlib.h>
#include <string.h>
#include "nmimp.h"

/*
 * Marshal/UnMarshal functions: Each takes a pointer to a buffer pointer and
 * adjusts the buffer pointer before exiting.  The UnMarshal functions check
 * that the tag byte's type field is correct for the function called and
 * return NMERROR_PROTOCOL if not.  If this error is returned, the buffer
 * pointer is NOT advanced, so a generic "scan past" function can be called to
 * consume the improper data, if need be.
 *
 * All Marshal/UnMarshal funtions return NMSTAT_SUCCESS for success and an
 * NMERROR_xxx code otherwise.  Even in error cases, they try to do their best.
 *
 * Notice that the sign of an integer value affects its encoding.  This cannot
 * be avoided, since we want to be able to unmarshal any encoded integer into
 * any integer storage type, so we need to know whether or not to perform sign
 * extension.  The advantage of having so many integer representations is that
 * smaller magnitudes can be stored compactly in get/set responses and, more
 * importantly, configuration database records and event records.  On the NM
 * application side, it's still possible to marshal all integers from four-byte
 * variables, and to unmarshal all integers into four-byte variables,
 * simplifying the variations on integer marshaling and unmarshaling.
 *
 * Notice, too, how some functions use the fact that the IMP tag byte's unit
 * field is (size - 1) to calculate offsets for oversized input elements.
 */

/* NmMarshalNull is used to marshal a NULL code.
 */
tNMSTAT NmMarshalNull(u8** ppBuf)
{
	u8*	pDst;

	pDst = *ppBuf;
	*pDst++ = IMP_TAG_NULL;
	*pDst++ = 0;
	*ppBuf = pDst;
	return (NMSTAT_SUCCESS);
}

/* NmUnMarshalNull is used to unmarshal a NULL code.
 */
tNMSTAT NmUnMarshalNull(u8** ppBuf)
{
	tNMSTAT	NmStat;

	if (**ppBuf == IMP_TYPE_NULL)
	{
		NmStat = NMSTAT_SUCCESS;
		*ppBuf += 2;
	}
	else
	{
		NmStat = NMERROR_PROTOCOL;
	}
	return (NmStat);
}

/* NmMarshalU8 is used to marshal a one-byte unsigned integer variable.
 */
tNMSTAT NmMarshalU8(u8** ppBuf, u8 Value)
{
	u8*	pDst;

	pDst = *ppBuf;
	*pDst++ = IMP_TAG_UINT_1;
	*pDst++ = Value;
	*ppBuf = pDst;
	return (NMSTAT_SUCCESS);
}

/* NmMarshalS8 is used to marshal a one-byte signed integer variable.
 *
 * Note: Obviously NmMarshalU8 and NmMarshalS8 are identical, except for their
 * type codes.  But, since these are short functions and heavily used, we keep
 * them separate instead of combining them into a single function that takes a
 * type parameter (and is thus a little bit slower).
 */
tNMSTAT NmMarshalS8(u8** ppBuf, s8 Value)
{
	u8*	pDst;

	pDst = *ppBuf;
	*pDst++ = IMP_TAG_SINT_1;
	*pDst++ = (u8)Value;
	*ppBuf = pDst;
	return (NMSTAT_SUCCESS);
}

/* NmUnMarshalU8 is used to unmarshal an integer value into a one-byte
 * unsigned integer variable.
 *
 * Notes: Since we're storing to a single byte, sign extension is not a factor
 * here.  NmUnMarshalS8 is just a macro that invokes this function.
 */
tNMSTAT NmUnMarshalU8(u8** ppBuf, u8* pValue)
{
	tNMSTAT	NmStat;
	u8*	pSrc;
	u8	Tag;
	u8	Type;

	pSrc = *ppBuf;
	Tag = *pSrc++;
	Type = Tag & IMP_ARRAY_TYPE_MASK;
	if ((Type == IMP_TYPE_UINT) || (Type == IMP_TYPE_SINT))
	{
		/* Skip most significant bytes, if any.
		 */
		pSrc += (Tag & IMP_UNIT_MASK);
		*pValue = *pSrc++;
		*ppBuf = pSrc;
		NmStat = NMSTAT_SUCCESS;
	}
	else
	{
		NmStat = NMERROR_PROTOCOL;
	}
	return (NmStat);
}

/* NmMarshalU16 is used to marshal a two-byte unsigned integer variable.
 */
tNMSTAT NmMarshalU16(u8** ppBuf, u16 Value)
{
	u8*	pDst;

	pDst = *ppBuf;
	if (Value < 256)
	{
		*pDst++ = IMP_TAG_UINT_1;
	}
	else
	{
		*pDst++ = IMP_TAG_UINT_2;
		*pDst++ = (u8)(Value >> 8);
	}
	*pDst++ = (u8)Value;
	*ppBuf = pDst;
	return (NMSTAT_SUCCESS);
}

/* NmMarshalS16 is used to marshal a two-byte signed integer variable.
 *
 * Note: Unlike the U8 and S8 functions, NmMarshalU16 and NmMarshalS16 are
 * different in more than just the type they use for encoding.  The range
 * checks differ too: 0 to 255 for unsigned byte; -128 to 127 for signed byte.
 */
tNMSTAT NmMarshalS16(u8** ppBuf, s16 Value)
{
	u8*	pDst;

	pDst = *ppBuf;
	if ((Value >= -128) && (Value <= 127))
	{
		*pDst++ = IMP_TAG_SINT_1;
	}
	else
	{
		*pDst++ = IMP_TAG_SINT_2;
		*pDst++ = (u8)(Value >> 8);
	}
	*pDst++ = (u8)Value;
	*ppBuf = pDst;
	return (NMSTAT_SUCCESS);
}

/* NmUnMarshalU16 is used to unmarshal an integer value into a two-byte
 * unsigned integer variable.
 *
 * Note: Even with sign extension, NmUnMarshalU16 and NmUnMarshalS16 are the
 * same, except for the type of pValue, since only the size of the variable
 * into which we're unmarshaling matters.  So NmUnMarshalS16 is a macro.
 */
tNMSTAT NmUnMarshalU16(u8** ppBuf, u16* pValue)
{
	tNMSTAT	NmStat;
	u8*	pSrc;
	u8	Tag;
	u8	Type;
	u8	Unit;
	u16	Value;

	pSrc = *ppBuf;
	Tag = *pSrc++;
	Type = Tag & IMP_ARRAY_TYPE_MASK;
	if ((Type == IMP_TYPE_UINT) || (Type == IMP_TYPE_SINT))
	{
		Unit = Tag & IMP_UNIT_MASK;
		if (Unit == IMP_UNIT_1)
		{
			Value = (u16)(*pSrc++); /* Unsigned: 0 to 255 */
			if ((Type == IMP_TYPE_SINT) && (Value & 0x080))
			{
				/* Sign extension for -1 to -128.
				 */
				Value |= (u16)0x0ff00;
			}
		}
		else
		{
			/* Skip most significant bytes, if any.
			 */
			pSrc += (Unit - 1);
			Value = (u16)((*pSrc++) << 8);
			Value |= (u16)(*pSrc++);
		}
		*pValue = Value;
		*ppBuf = pSrc;
		NmStat = NMSTAT_SUCCESS;
	}
	else
	{
		NmStat = NMERROR_PROTOCOL;
	}
	return (NmStat);
}

/* NmMarshalU32 is used to marshal a four-byte unsigned integer variable.
 */
tNMSTAT NmMarshalU32(u8** ppBuf, u32 Value)
{
	u8*	pDst;

	pDst = *ppBuf;
	if (Value < 256)
	{
		*pDst++ = IMP_TAG_UINT_1;
	}
	else if (Value < 65536)
	{
		*pDst++ = IMP_TAG_UINT_2;
		*pDst++ = (u8)(Value >> 8);
	}
	else
	{
		*pDst++ = IMP_TAG_UINT_4;
		*pDst++ = (u8)(Value >> 24);
		*pDst++ = (u8)(Value >> 16);
		*pDst++ = (u8)(Value >> 8);
	}
	*pDst++ = (u8)Value;
	*ppBuf = pDst;
	return (NMSTAT_SUCCESS);
}

/* NmMarshalS32 is used to marshal a four-byte signed integer variable.
 */
tNMSTAT NmMarshalS32(u8** ppBuf, s32 Value)
{
	u8*	pDst;

	pDst = *ppBuf;
	if ((Value >= -128) && (Value <= 127))
	{
		*pDst++ = IMP_TAG_SINT_1;
	}
	else if ((Value >= -32768) && (Value <= 32767))
	{
		*pDst++ = IMP_TAG_SINT_2;
		*pDst++ = (u8)(Value >> 8);
	}
	else
	{
		*pDst++ = IMP_TAG_SINT_4;
		*pDst++ = (u8)(Value >> 24);
		*pDst++ = (u8)(Value >> 16);
		*pDst++ = (u8)(Value >> 8);
	}
	*pDst++ = (u8)Value;
	*ppBuf = pDst;
	return (NMSTAT_SUCCESS);
}

/* NmUnMarshalU32 is used to unmarshal an integer value into a four-byte
 * unsigned integer variable.
 *
 * Note: Even with sign extension, NmUnMarshalU32 and NmUnMarshalS32 are the
 * same, except for the type of pValue, since only the size of the variable
 * into which we're unmarshaling matters.  So NmUnMarshalS32 is a macro.
 */
tNMSTAT NmUnMarshalU32(u8** ppBuf, u32* pValue)
{
	tNMSTAT	NmStat;
	u8*	pSrc;
	u8	Tag;
	u8	Type;
	u32	Value;

	pSrc = *ppBuf;
	Tag = *pSrc++;
	Type = Tag & IMP_ARRAY_TYPE_MASK;
	if ((Type == IMP_TYPE_UINT) || (Type == IMP_TYPE_SINT))
	{
		switch (Tag & IMP_UNIT_MASK)
		{
		  case IMP_UNIT_4:
			Value = (u32)(*pSrc++);
			Value <<= 8;
			Value |= (u32)(*pSrc++);
			Value <<= 8;
			Value |= (u32)(*pSrc++);
			Value <<= 8;
			Value |= (u32)(*pSrc++);
			break;
		  case IMP_UNIT_3:
			Value = (u32)(*pSrc++);
			Value <<= 8;
			Value |= (u32)(*pSrc++);
			Value <<= 8;
			Value |= (u32)(*pSrc++);
			if ((Type == IMP_TYPE_SINT) && (Value & (u32)0x0800000))
			{
				/* Sign extension.
				 */
				Value |= (u32)0x0ff000000;
			}
			break;
		  case IMP_UNIT_2:
			Value = (u32)(*pSrc++);
			Value <<= 8;
			Value |= (u32)(*pSrc++);
			if ((Type == IMP_TYPE_SINT) && (Value & (u32)0x08000))
			{
				/* Sign extension.
				 */
				Value |= (u32)0x0ffff0000;
			}
			break;
		  case IMP_UNIT_1:
			Value = (u32)(*pSrc++);
			if ((Type == IMP_TYPE_SINT) && (Value & (u32)0x080))
			{
				/* Sign extension.
				 */
				Value |= (u32)0x0ffffff00;
			}
			break;
		  default:
			Value = 0;
			break;
		}
		*pValue = Value;
		*ppBuf = pSrc;
		NmStat = NMSTAT_SUCCESS;
	}
	else
	{
		NmStat = NMERROR_PROTOCOL;
	}
	return (NmStat);
}

/* NmMarshalByteArray is used to marshal an array of one-byte integer
 * variables, using the appropriate array tag.
 *
 * Note: NmMarshalArrayU8 and NmMarshalArrayS8 are macros that call this
 * function.
 */
tNMSTAT NmMarshalByteArray(u8** ppBuf, u8* pArray, u16 ArrayLen, u8 Tag)
{
	tNMSTAT	NmStat;
	u8*	pDst;
	u8*	pSrc;
	u8*	pSrcEnd;
	u16	Length;

	if (ArrayLen > MAX_ARRAY1_LENGTH)
	{
		NmStat = NMERROR_ARRAY_TOO_BIG;
		Length = MAX_ARRAY1_LENGTH;
	}
	else
	{
		NmStat = NMSTAT_SUCCESS;
		Length = (u16)ArrayLen;
	}
	pDst = *ppBuf;
	*pDst++ = Tag;
	*pDst++ = (u8)(Length >> 8);
	*pDst++ = (u8)Length;
	pSrc = pArray;
	pSrcEnd = pSrc + Length;
	if (pSrc < pSrcEnd) do
	{
		*pDst++ = *pSrc;
	} while (++pSrc < pSrcEnd);
	*ppBuf = pDst;
	return (NmStat);
}

/* NmUnMarshalArrayU8 is used to unmarshal an array of integers into an array
 * of one-byte unsigned integer variables.
 *
 * Notes: There is no sign extension here.  NmUnMarshalArrayS8 is a macro that
 * calls this function.
 */
tNMSTAT NmUnMarshalArrayU8(u8** ppBuf, u8* pArray, u16* pArrayLen, u16 BufLen)
{
	tNMSTAT	NmStat;
	u8*	pSrc;
	u8*	pSrcEnd;
	u8*	pDst;
	u8*	pDstEnd;
	u16	Length;
	u8	Tag;
	u8	Type;
	u8	Unit;
	ix	Offset;

	pSrc = *ppBuf;
	Tag = *pSrc++;
	Type = Tag & IMP_ARRAY_TYPE_MASK;
	if ((Type == IMP_ARRAY_UINT) || (Type == IMP_ARRAY_SINT))
	{
		NmStat = NMSTAT_SUCCESS;
		Unit = Tag & IMP_UNIT_MASK;
		Length = *pSrc++;
		Length <<= 8;
		Length |= *pSrc++;
		pSrcEnd = pSrc + (Length * (Unit + 1));
		Offset = (ix)Unit;
		pDst = pArray;
		pDstEnd = pDst + BufLen;
		if ((pDst < pDstEnd) && (pSrc < pSrcEnd)) do
		{
			/* Adjust source to discard most significant
			 * bytes (if any).
			 */
			pSrc += Offset;
			*pDst++ = *pSrc++;
		} while ((pDst < pDstEnd) && (pSrc < pSrcEnd));
		if (pSrc < pSrcEnd)
		{
			NmStat = NMERROR_ARRAY_MISMATCH;
		}
		*ppBuf = pSrcEnd;
		*pArrayLen = Length;
	}
	else
	{
		NmStat = NMERROR_PROTOCOL;
		*pArrayLen = 0;
	}
	return (NmStat);
}

/* NmMarshalWordArray is used to marshal an array of two-byte integer
 * variables, using the appropriate array tag.
 *
 * Notes: Array encodings do not compress small-valued entries.
 * NmMarshalArrayU16 and NmMarshalArrayS16 are macros that call this function.
 */
tNMSTAT NmMarshalWordArray(u8** ppBuf, u16* pArray, u16 ArrayLen, u8 Tag)
{
	tNMSTAT	NmStat;
	u8*	pDst;
	u16*	pSrc;
	u16*	pSrcEnd;
	u16	Length;
	u16	Value;

	if (ArrayLen > MAX_ARRAY2_LENGTH)
	{
		NmStat = NMERROR_ARRAY_TOO_BIG;
		Length = MAX_ARRAY2_LENGTH;
	}
	else
	{
		NmStat = NMSTAT_SUCCESS;
		Length = (u16)ArrayLen;
	}
	pDst = *ppBuf;
	*pDst++ = Tag;
	*pDst++ = (u8)(Length >> 8);
	*pDst++ = (u8)Length;
	pSrc = pArray;
	pSrcEnd = pSrc + Length;
	if (pSrc < pSrcEnd) do
	{
		Value = *pSrc;
		*pDst++ = (u8)(Value >> 8);
		*pDst++ = (u8)Value;
	} while (++pSrc < pSrcEnd);
	*ppBuf = pDst;
	return (NmStat);
}

/* NmUnMarshalArrayU16 is used to unmarshal an array of integers into an array
 * of two-byte integer variables.
 *
 * Note: NmUnMarshalArrayS16 is a macro that calls this function.
 */
tNMSTAT NmUnMarshalArrayU16(u8** ppBuf, u16* pArray, u16* pArrayLen, u16 BufLen)
{
	tNMSTAT	NmStat;
	u8*	pSrc;
	u8*	pSrcEnd;
	u16*	pDst;
	u16*	pDstEnd;
	u16	Value;
	u16	Length;
	u8	Tag;
	u8	Type;
	u8	Unit;
	ix	Offset;

	pSrc = *ppBuf;
	Tag = *pSrc++;
	Type = Tag & IMP_ARRAY_TYPE_MASK;
	if ((Type == IMP_ARRAY_UINT) || (Type == IMP_ARRAY_SINT))
	{
		NmStat = NMSTAT_SUCCESS;
		Unit = Tag & IMP_UNIT_MASK;
		Length = *pSrc++;
		Length <<= 8;
		Length |= *pSrc++;
		pSrcEnd = pSrc + (Length * (Unit + 1));
		pDst = pArray;
		pDstEnd = pDst + BufLen;
		if (Unit == IMP_UNIT_1)
		{
			if ((pDst < pDstEnd) && (pSrc < pSrcEnd)) do
			{
				Value = (u16)(*pSrc++);
				if ((Type == IMP_ARRAY_SINT) &&
				    (Value & 0x080))
				{
					/* Sign extension.
					 */
					Value |= (u16)0x0ff00;
				}
				*pDst++ = Value;
			} while ((pDst < pDstEnd) && (pSrc < pSrcEnd));
		}
		else
		{
			Offset = (ix)(Unit - 1);
			if ((pDst < pDstEnd) && (pSrc < pSrcEnd)) do
			{
				/* Adjust source to discard most
				 * significant bytes (if any).
				 */
				pSrc += Offset;
				Value = (u16)((*pSrc++) << 8);
				Value |= (u16)(*pSrc++);
				*pDst++ = Value;
			} while ((pDst < pDstEnd) && (pSrc < pSrcEnd));
		}
		if (pSrc < pSrcEnd)
		{
			NmStat = NMERROR_ARRAY_MISMATCH;
		}
		*ppBuf = pSrcEnd;
		*pArrayLen = Length;
	}
	else
	{
		NmStat = NMERROR_PROTOCOL;
		*pArrayLen = 0;
	}
	return (NmStat);
}

/* NmMarshalDWordArray is used to marshal an array of four-byte integer
 * variables, using the appropriate array tag.
 *
 * Notes: Array encodings do not compress small-valued entries.
 * NmMarshalArrayU32 and NmMarshalArrayS32 are macros that call this function.
 */
tNMSTAT NmMarshalDWordArray(u8** ppBuf, u32* pArray, u16 ArrayLen, u8 Tag)
{
	tNMSTAT	NmStat;
	u8*	pDst;
	u32*	pSrc;
	u32*	pSrcEnd;
	u16	Length;
	u32	Value;

	if (ArrayLen > MAX_ARRAY4_LENGTH)
	{
		NmStat = NMERROR_ARRAY_TOO_BIG;
		Length = MAX_ARRAY4_LENGTH;
	}
	else
	{
		NmStat = NMSTAT_SUCCESS;
		Length = (u16)ArrayLen;
	}
	pDst = *ppBuf;
	*pDst++ = Tag;
	*pDst++ = (u8)(Length >> 8);
	*pDst++ = (u8)Length;
	pSrc = pArray;
	pSrcEnd = pSrc + Length;
	if (pSrc < pSrcEnd) do
	{
		Value = *pSrc;
		*pDst++ = (u8)(Value >> 24);
		*pDst++ = (u8)(Value >> 16);
		*pDst++ = (u8)(Value >> 8);
		*pDst++ = (u8)Value;
	} while (++pSrc < pSrcEnd);
	*ppBuf = pDst;
	return (NmStat);
}

/* NmUnMarshalArrayU32 is used to unmarshal an array of integers into an array
 * of four-byte integer variables.
 *
 * Note: NmUnMarshalArrayS32 is a macro that calls this function.
 */
tNMSTAT NmUnMarshalArrayU32(u8** ppBuf, u32* pArray, u16* pArrayLen, u16 BufLen)
{
	tNMSTAT	NmStat;
	u8*	pSrc;
	u8*	pSrcEnd;
	u32*	pDst;
	u32*	pDstEnd;
	u32	Value;
	u16	Length;
	u8	Tag;
	u8	Type;
	u8	Unit;

	pSrc = *ppBuf;
	Tag = *pSrc++;
	Type = Tag & IMP_ARRAY_TYPE_MASK;
	if ((Type == IMP_ARRAY_UINT) || (Type == IMP_ARRAY_SINT))
	{
		NmStat = NMSTAT_SUCCESS;
		Unit = Tag & IMP_UNIT_MASK;
		Length = *pSrc++;
		Length <<= 8;
		Length |= *pSrc++;
		pSrcEnd = pSrc + (Length * (Unit + 1));
		pDst = pArray;
		pDstEnd = pDst + BufLen;
		if ((pDst < pDstEnd) && (pSrc < pSrcEnd)) do
		{
			switch (Unit)
			{
			  case IMP_UNIT_4:
				Value = (u32)(*pSrc++);
				Value <<= 8;
				Value |= (u32)(*pSrc++);
				Value <<= 8;
				Value |= (u32)(*pSrc++);
				Value <<= 8;
				Value |= (u32)(*pSrc++);
				break;
			  case IMP_UNIT_3:
				Value = (u32)(*pSrc++);
				Value <<= 8;
				Value |= (u32)(*pSrc++);
				Value <<= 8;
				Value |= (u32)(*pSrc++);
				if ((Type == IMP_ARRAY_SINT) &&
				    (Value & (u32)0x0800000))
				{
					/* Sign extension.
					 */
					Value |= (u32)0x0ff000000;
				}
				break;
			  case IMP_UNIT_2:
				Value = (u32)(*pSrc++);
				Value <<= 8;
				Value |= (u32)(*pSrc++);
				if ((Type == IMP_ARRAY_SINT) &&
				    (Value & (u32)0x08000))
				{
					/* Sign extension.
					 */
					Value |= (u32)0x0ffff0000;
				}
				break;
			  case IMP_UNIT_1:
				Value = (u32)(*pSrc++);
				if ((Type == IMP_ARRAY_SINT) &&
				    (Value & (u32)0x080))
				{
					/* Sign extension.
					 */
					Value |= (u32)0x0ffffff00;
				}
				break;
			  default:
				Value = 0;
				break;
			}
			*pDst++ = Value;
		} while ((pDst < pDstEnd) && (pSrc < pSrcEnd));
		if (pSrc < pSrcEnd)
		{
			NmStat = NMERROR_ARRAY_MISMATCH;
		}
		*ppBuf = pSrcEnd;
		*pArrayLen = Length;
	}
	else
	{
		NmStat = NMERROR_PROTOCOL;
		*pArrayLen = 0;
	}
	return (NmStat);
}

/* NmMarshalString is used to marshal a null-terminated character string.
 */
tNMSTAT NmMarshalString(u8** ppBuf, char* pChars)
{
	u8*	pMark;
	char*	pSrc;
	char*	pDst;
	u16	Length;

	pMark = *ppBuf;
	*pMark++ = IMP_TAG_CHAR_STRING;
	/* Encode length at pMark later! */
	pDst = (char*)(pMark + 2);
	pSrc = pChars;
	while ((*pDst++ = *pSrc++) != '\0'); /* Classic string copy. */
	Length = (u16)(pSrc - pChars);
	*pMark++ = (u8)(Length >> 8);
	*pMark = (u8)Length;
	*ppBuf = (u8*)pDst;
	return (NMSTAT_SUCCESS);
}

/* NmUnMarshalString is used to unmarshal a null-terminated character string.
 * It returns the string in a newly-allocated buffer.
 */
tNMSTAT NmUnMarshalString(u8** ppBuf, char** ppChars)
{
	tNMSTAT	NmStat;
	u8*	pSrc;
	u8	Tag;
	u16	Length;
	char*	pChars;
	char*	pNull;

	pSrc = *ppBuf;
	Tag = *pSrc++;
	if ((Tag & IMP_ARRAY_TYPE_MASK) == IMP_ARRAY_CHAR_STRING)
	{
		Length = *pSrc++;
		Length <<= 8;
		Length |= *pSrc++;
		pChars = (char*)calloc(1, Length);
		if (pChars != (char*)0)
		{
			strncpy(pChars, (char*)pSrc, Length);
			pNull = pChars + (Length - 1);
			/*
			 * Make sure string is null-terminated.
			 */
			*pNull = '\0';
			NmStat = NMSTAT_SUCCESS;
		}
		else
		{
			NmStat = NMERROR_MEMORY;
		}
		*ppChars = pChars;
		*ppBuf = pSrc + Length;
	}
	else
	{
		NmStat = NMERROR_PROTOCOL;
	}
	return (NmStat);
}

/* NmMarshalObjectId is used to marshal an object id.  It takes advantage of
 * the fact that an Oid structure may be treated as an array of u16 values.
 * Note that tOid.IxCnt is not marshaled explicitly, since it may be inferred
 * from the array length.
 */
tNMSTAT NmMarshalObjectId(u8** ppBuf, tOid* pOid)
{
	u8*	pDst;
	u16*	pSrc;
	u16*	pSrcEnd;
	u16	Length;
	u16	Value;

	pDst = *ppBuf;
	*pDst++ = IMP_TAG_OBJECT_ID;
	Length = pOid->IxCnt + OID_NON_IX_U16_COUNT;
	*pDst++ = (u8)(Length >> 8);
	*pDst++ = (u8)Length;
	pSrc = &(pOid->ContId);
	pSrcEnd = pSrc + Length;
	do
	{
		Value = *pSrc;
		*pDst++ = (u8)(Value >> 8);
		*pDst++ = (u8)Value;
	} while (++pSrc < pSrcEnd);
	*ppBuf = pDst;
	return (NMSTAT_SUCCESS);
}

/* NmUnMarshalObjectId is used to unmarshal an object id.
 */
tNMSTAT NmUnMarshalObjectId(u8** ppBuf, tOid* pOid)
{
	tNMSTAT	NmStat;
	u8*	pSrc;
	u8	Tag;
	u16*	pDst;
	u16*	pDstEnd;
	u16	Length;
	u16	Value;

	pSrc = *ppBuf;
	Tag = *pSrc++;
	if ((Tag & IMP_ARRAY_TYPE_MASK) == IMP_ARRAY_OBJECT_ID)
	{
		NmStat = NMSTAT_SUCCESS;
		Length = *pSrc++;
		Length <<= 8;
		Length |= *pSrc++;
		*ppBuf = pSrc + (Length * sizeof(u16));
		if (Length > (OID_NON_IX_U16_COUNT + MAX_OID_LEVELS))
		{
			Length = (OID_NON_IX_U16_COUNT + MAX_OID_LEVELS);
			NmStat = NMERROR_OBJECT_ID;
		}
		if (Length >= OID_NON_IX_U16_COUNT)
		{
			pOid->IxCnt = Length - OID_NON_IX_U16_COUNT;
			pDst = &(pOid->ContId);
			pDstEnd = pDst + Length;
			do
			{
				Value = (u16)((*pSrc++) << 8);
				Value |= (u16)(*pSrc++);
				*pDst = Value;
			} while (++pDst < pDstEnd);
		}
		else
		{
			pOid->IxCnt = (u16)0;
			pOid->ContId = NM_CONTID_UNDEFINED;
			NmStat = NMERROR_OBJECT_ID;
		}
	}
	else
	{
		NmStat = NMERROR_PROTOCOL;
	}
	return (NmStat);
}

tNMSTAT NmMarshalClassId(u8** ppBuf, tCID Cid)
{
	u8*	pDst;

	pDst = *ppBuf;
	if (Cid < 256)
	{
		*pDst++ = IMP_TAG_CLASS_ID_1;
	}
	else
	{
		*pDst++ = IMP_TAG_CLASS_ID_2;
		*pDst++ = (u8)(Cid >> 8);
	}
	*pDst++ = (u8)Cid;
	*ppBuf = pDst;
	return (NMSTAT_SUCCESS);
}

tNMSTAT NmUnMarshalClassId(u8** ppBuf, tCID* pCid)
{
	tNMSTAT	NmStat;
	u8*	pSrc;
	u8	Tag;
	u8	Unit;
	tCID	Cid;

	pSrc = *ppBuf;
	Tag = *pSrc++;
	if ((Tag & IMP_ARRAY_TYPE_MASK) == IMP_TYPE_CLASS_ID)
	{
		NmStat = NMSTAT_SUCCESS;
		Unit = Tag & IMP_UNIT_MASK;
		if (Unit == IMP_UNIT_1)
		{
			*pCid = (tCID)(*pSrc++);
		}
		else if (Unit == IMP_UNIT_2)
		{
			Cid = (tCID)((*pSrc++) << 8);
			Cid |= (tCID)(*pSrc++);
			*pCid = Cid;
		}
		else
		{
			NmStat = NMERROR_ENCODING;
			*pCid = NM_CLASS_UNDEFINED;
			pSrc += (Unit + 1);
		}
		*ppBuf = pSrc;
	}
	else
	{
		NmStat = NMERROR_PROTOCOL;
	}
	return (NmStat);
}

tNMSTAT NmMarshalClassMember(u8** ppBuf, tCID Cid, u8 Member, u8 Type)
{
	u8*	pDst;

	pDst = *ppBuf;
	if (Cid < 256)
	{
		*pDst++ = Type | IMP_ARRAY_FALSE | IMP_UNIT_2;
	}
	else
	{
		*pDst++ = Type | IMP_ARRAY_FALSE | IMP_UNIT_3;
		*pDst++ = (u8)(Cid >> 8);
	}
	*pDst++ = (u8)Cid;
	*pDst++ = Member;
	*ppBuf = pDst;
	return (NMSTAT_SUCCESS);
}

tNMSTAT NmUnMarshalClassMember(u8** ppBuf, tCID* pCid, u8* pMember, u8 Type)
{
	tNMSTAT	NmStat;
	u8*	pSrc;
	u8	Tag;
	u8	Unit;
	tCID	Cid;

	pSrc = *ppBuf;
	Tag = *pSrc++;
	if ((Tag & IMP_ARRAY_TYPE_MASK) == Type)
	{
		NmStat = NMSTAT_SUCCESS;
		Unit = Tag & IMP_UNIT_MASK;
		if (Unit == IMP_UNIT_2)
		{
			*pCid = (tCID)(*pSrc++);
			*pMember = *pSrc++;
		}
		else if (Unit == IMP_UNIT_3)
		{
			Cid = (tCID)((*pSrc++) << 8);
			Cid |= (tCID)(*pSrc++);
			*pCid = Cid;
			*pMember = *pSrc++;
		}
		else
		{
			NmStat = NMERROR_ENCODING;
			*pCid = NM_CLASS_UNDEFINED;
			*pMember = (u8)0;
			pSrc += (Unit + 1);
		}
		*ppBuf = pSrc;
	}
	else
	{
		NmStat = NMERROR_PROTOCOL;
	}
	return (NmStat);
}

tNMSTAT NmMarshalStatusCode(u8** ppBuf, tNMSTAT Code)
{
	u8*	pDst;

	pDst = *ppBuf;
	*pDst++ = IMP_TAG_STATUS_CODE;
	*pDst++ = (u8)(Code >> 8);
	*pDst++ = (u8)Code;
	*ppBuf = pDst;
	return (NMSTAT_SUCCESS);
}

tNMSTAT NmUnMarshalStatusCode(u8** ppBuf, tNMSTAT* pCode)
{
	tNMSTAT	NmStat;
	u8*	pSrc;
	u8	Tag;
	u8	Unit;
	tNMSTAT	Code;

	pSrc = *ppBuf;
	Tag = *pSrc++;
	if ((Tag & IMP_ARRAY_TYPE_MASK) == IMP_TYPE_STATUS_CODE)
	{
		NmStat = NMSTAT_SUCCESS;
		Unit = Tag & IMP_UNIT_MASK;
		if (Unit == IMP_UNIT_2)
		{
			Code = (tNMSTAT)((*pSrc++) << 8);
			Code |= (tNMSTAT)(*pSrc++);
			*pCode = Code;
		}
		else
		{
			NmStat = NMERROR_ENCODING;
			*pCode = NM_CLASS_UNDEFINED;
			pSrc += (Unit + 1);
		}
		*ppBuf = pSrc;
	}
	else
	{
		NmStat = NMERROR_PROTOCOL;
	}
	return (NmStat);
}

tNMSTAT NmMarshalEndOfMsg(u8** ppBuf, u32 CkSum, u8 UseCkSum)
{
	u8*	pDst;

	pDst = *ppBuf;
	if (!UseCkSum)
	{
		*pDst++ = IMP_TAG_END_OF_MSG_1;
		CkSum = 0;
	}
	else if (CkSum < 65536)
	{
		*pDst++ = IMP_TAG_END_OF_MSG_2;
		*pDst++ = (u8)(CkSum >> 8);
	}
	else
	{
		*pDst++ = IMP_TAG_END_OF_MSG_4;
		*pDst++ = (u8)(CkSum >> 24);
		*pDst++ = (u8)(CkSum >> 16);
		*pDst++ = (u8)(CkSum >> 8);
	}
	*pDst++ = (u8)CkSum;
	*ppBuf = pDst;
	return (NMSTAT_SUCCESS);
}

tNMSTAT NmUnMarshalEndOfMsg(u8** ppBuf, u32* pCkSum, u8* pUseCkSum)
{
	tNMSTAT	NmStat;
	u8*	pSrc;
	u8	Tag;
	u8	Unit;
	u32	CkSum;

	pSrc = *ppBuf;
	Tag = *pSrc++;
	if ((Tag & IMP_ARRAY_TYPE_MASK) == IMP_TYPE_END_OF_MSG)
	{
		NmStat = NMSTAT_SUCCESS;
		Unit = Tag & IMP_UNIT_MASK;
		CkSum = 0;
		switch (Unit)
		{
		  case IMP_UNIT_4:
			CkSum = (u32)(*pSrc++);
			CkSum <<= 8;
			CkSum |= (u32)(*pSrc++);
			CkSum <<= 8;
		  case IMP_UNIT_2:
			CkSum |= (u32)(*pSrc++);
			CkSum <<= 8;
			CkSum |= (u32)(*pSrc++);
			*pUseCkSum = TRUE;
			break;
		  case IMP_UNIT_3:
		  default:
			NmStat = NMERROR_ENCODING;
		  case IMP_UNIT_1:
			pSrc += (Unit + 1); /* Skip the bogus checksum field. */
			*pUseCkSum = FALSE;
			break;
		}
		*pCkSum = CkSum;
		*ppBuf = pSrc;
	}
	else
	{
		NmStat = NMERROR_PROTOCOL;
	}
	return (NmStat);
}

tNMSTAT NmMarshalUserId(u8** ppBuf, u32 Uid)
{
	u8*	pDst;

	pDst = *ppBuf;
	*pDst++ = IMP_TAG_USER_ID;
	*pDst++ = (u8)(Uid >> 24);
	*pDst++ = (u8)(Uid >> 16);
	*pDst++ = (u8)(Uid >> 8);
	*pDst++ = (u8)Uid;
	*ppBuf = pDst;
	return (NMSTAT_SUCCESS);
}

tNMSTAT NmUnMarshalUserId(u8** ppBuf, u32* pUid)
{
	tNMSTAT	NmStat;
	u8*	pSrc;
	u8	Tag;
	u8	Unit;
	u32	Uid;

	pSrc = *ppBuf;
	Tag = *pSrc++;
	if ((Tag & IMP_ARRAY_TYPE_MASK) == IMP_TYPE_USER_ID)
	{
		NmStat = NMSTAT_SUCCESS;
		Unit = Tag & IMP_UNIT_MASK;
		if (Unit == IMP_UNIT_4)
		{
			NmStat = NMSTAT_SUCCESS;
			Uid = (u32)(*pSrc++);
			Uid <<= 8;
			Uid |= (u32)(*pSrc++);
			Uid <<= 8;
			Uid |= (u32)(*pSrc++);
			Uid <<= 8;
			Uid |= (u32)(*pSrc++);
		}
		else
		{
			NmStat = NMERROR_ENCODING;
			Uid = 0; /* Same as four ASCII nulls. */
			pSrc += (Unit + 1); /* Skip unused bytes. */
		}
		*pUid = Uid;
		*ppBuf = pSrc;
	}
	else
	{
		NmStat = NMERROR_PROTOCOL;
	}
	return (NmStat);
}

tNMSTAT NmMarshalComparator(u8** ppBuf, u8 Comp)
{
	u8*	pDst;

	pDst = *ppBuf;
	*pDst++ = IMP_TAG_COMPARATOR;
	*pDst++ = Comp;
	*ppBuf = pDst;
	return (NMSTAT_SUCCESS);
}

tNMSTAT NmUnMarshalComparator(u8** ppBuf, u8* pComp)
{
	tNMSTAT	NmStat;
	u8*	pSrc;
	u8	Tag;
	u8	Unit;

	pSrc = *ppBuf;
	Tag = *pSrc++;
	if ((Tag & IMP_ARRAY_TYPE_MASK) == IMP_TYPE_COMPARATOR)
	{
		NmStat = NMSTAT_SUCCESS;
		Unit = Tag & IMP_UNIT_MASK;
		if (Unit == IMP_UNIT_1)
		{
			NmStat = NMSTAT_SUCCESS;
			*pComp = *pSrc++;
		}
		else
		{
			NmStat = NMERROR_ENCODING;
			*pComp = (u8)0;
			pSrc += (Unit + 1); /* Skip unused bytes. */
		}
		*ppBuf = pSrc;
	}
	else
	{
		NmStat = NMERROR_PROTOCOL;
	}
	return (NmStat);
}

tNMSTAT NmMarshalAppAddr(u8** ppBuf, u8* pAddr, u16 AddrLen)
{
	tNMSTAT	NmStat;
	u8*	pDst;
	u8*	pSrc;
	u8*	pSrcEnd;
	u16	Length;

	if (AddrLen > MAX_ARRAY1_LENGTH)
	{
		NmStat = NMERROR_APP_ADDR;
		Length = MAX_ARRAY1_LENGTH;
	}
	else
	{
		NmStat = NMSTAT_SUCCESS;
		Length = (u16)AddrLen;
	}
	pDst = *ppBuf;
	*pDst++ = IMP_TAG_APP_ADDR;
	*pDst++ = (u8)(Length >> 8);
	*pDst++ = (u8)Length;
	pSrc = pAddr;
	pSrcEnd = pSrc + Length;
	if (pSrc < pSrcEnd) do
	{
		*pDst++ = *pSrc;
	} while (++pSrc < pSrcEnd);
	*ppBuf = pDst;
	return (NmStat);
}

tNMSTAT NmUnMarshalAppAddr(u8** ppBuf, u8* pAddr, u16* pAddrLen, u16 BufLen)
{
	tNMSTAT	NmStat;
	u8*	pSrc;
	u8*	pSrcEnd;
	u8*	pDst;
	u8*	pDstEnd;
	u16	Length;
	u8	Tag;
	u8	Unit;

	pSrc = *ppBuf;
	Tag = *pSrc++;
	if ((Tag & IMP_ARRAY_TYPE_MASK) == IMP_ARRAY_APP_ADDR)
	{
		NmStat = NMSTAT_SUCCESS;
		Unit = Tag & IMP_UNIT_MASK;
		Length = *pSrc++;
		Length <<= 8;
		Length |= *pSrc++;
		pDst = pAddr;
		pDstEnd = pDst + BufLen;
		if ((Unit == IMP_UNIT_1) && (Length <= BufLen))
		{
			pSrcEnd = pSrc + Length;
			if ((pDst < pDstEnd) && (pSrc < pSrcEnd)) do
			{
				*pDst++ = *pSrc++;
			} while ((pDst < pDstEnd) && (pSrc < pSrcEnd));
			*pAddrLen = Length;
		}
		else
		{
			NmStat = NMERROR_APP_ADDR;
			pSrcEnd = pSrc + (Length * (Unit + 1));
			*pAddrLen = (u16)0;
		}
		*ppBuf = pSrcEnd;
	}
	else
	{
		NmStat = NMERROR_PROTOCOL;
	}
	return (NmStat);
}

/* NmMarshalLength is used to marshal a two-byte length value.  Notice that,
 * unlike the integer marshaling functions, this one always uses two bytes so
 * that the size of the marshaled data is predictable.
 */
tNMSTAT NmMarshalLength(u8** ppBuf, tNMLEN Len)
{
	u8*	pDst;

	pDst = *ppBuf;
	*pDst++ = IMP_TAG_LENGTH;
	*pDst++ = (u8)(Len >> 8);
	*pDst++ = (u8)Len;
	*ppBuf = pDst;
	return (NMSTAT_SUCCESS);
}

/* NmUnMarshalLength is used to unmarshal a two-byte length value.
 */
tNMSTAT NmUnMarshalLength(u8** ppBuf, tNMLEN* pLen)
{
	tNMSTAT	NmStat;
	u8*	pSrc;
	u8	Tag;
	u8	Unit;
	u16	Len;

	pSrc = *ppBuf;
	Tag = *pSrc++;
	if ((Tag & IMP_ARRAY_TYPE_MASK) == IMP_TYPE_LENGTH)
	{
		NmStat = NMSTAT_SUCCESS;
		Unit = Tag & IMP_UNIT_MASK;
		if (Unit == IMP_UNIT_1)
		{
			Len = (u16)(*pSrc++);
		}
		else
		{
			/* Skip most significant bytes, if any.
			 */
			pSrc += (Unit - 1);
			Len = (u16)((*pSrc++) << 8);
			Len |= (u16)(*pSrc++);
		}
		*pLen = Len;
		*ppBuf = pSrc;
	}
	else
	{
		NmStat = NMERROR_PROTOCOL;
	}
	return (NmStat);
}

tNMSTAT NmMarshalImpHeader(u8** ppBuf, tIMPHDR* pImpHdr)
{
	tNMSTAT	NmStat;
	u8*	pMsgLen;
	u8*	pHdrLen;
	tNMLEN	MsgLen;
	tNMLEN	HdrLen;

	/* Trick: We need to set aside space for the message and header
	 * lengths, then actually record them in the message later, when
	 * they're known for sure.  So, we estimate them, marshal them, and
	 * remarshal them once they're known.
	 */
	pMsgLen = *ppBuf;
	if ((NmStat = NmMarshalLength(ppBuf, 1000)) == NMSTAT_SUCCESS)
	{
		pHdrLen = *ppBuf;
		if (((NmStat = NmMarshalLength(ppBuf, 1000) ==
			NMSTAT_SUCCESS)) &&
		    ((NmStat = NmMarshalU16(ppBuf, pImpHdr->OpCode)) ==
			NMSTAT_SUCCESS) &&
		    ((NmStat = NmMarshalStatusCode(ppBuf, pImpHdr->StatCode)) ==
			NMSTAT_SUCCESS) &&
		    ((NmStat = NmMarshalObjectId(ppBuf, &(pImpHdr->ObjId))) ==
			NMSTAT_SUCCESS) &&
		    ((NmStat = NmMarshalUserId(ppBuf, pImpHdr->Uid)) ==
			NMSTAT_SUCCESS) &&
		    ((NmStat = NmMarshalU32(ppBuf, pImpHdr->Seq)) ==
			NMSTAT_SUCCESS) &&
		    ((NmStat = NmMarshalU32(ppBuf, pImpHdr->TransId1)) ==
			NMSTAT_SUCCESS) &&
		    ((NmStat = NmMarshalU32(ppBuf, pImpHdr->TransId2)) ==
			NMSTAT_SUCCESS) &&
		    ((NmStat = NmMarshalU16(ppBuf, pImpHdr->Access)) ==
			NMSTAT_SUCCESS) &&
		    ((NmStat = NmMarshalU16(ppBuf, pImpHdr->Link)) ==
			NMSTAT_SUCCESS) &&
		    ((NmStat = NmMarshalAppAddr(ppBuf, pImpHdr->AppAddr,
			pImpHdr->AppAddrLen)) == NMSTAT_SUCCESS))
		{
			HdrLen = (tNMLEN)(*ppBuf - pMsgLen);
			MsgLen = HdrLen + pImpHdr->RespBodyLen;
			if (((NmStat = NmMarshalLength(&pMsgLen, MsgLen)) ==
				NMSTAT_SUCCESS) &&
			    ((NmStat = NmMarshalLength(&pHdrLen, HdrLen)) ==
				NMSTAT_SUCCESS))
			{
				/* Update the caller's IMP header structure.
				 */
				pImpHdr->MsgLen = MsgLen;
				pImpHdr->HdrLen = HdrLen;
			}
		}
	}
	return (NmStat);
}

tNMSTAT NmUnMarshalImpHeader(u8** ppBuf, tIMPHDR* pImpHdr)
{
	tNMSTAT	NmStat;

	bzero((char*)pImpHdr, sizeof(tIMPHDR));
	if (((NmStat = NmUnMarshalLength(ppBuf, &(pImpHdr->MsgLen))) ==
		NMSTAT_SUCCESS) &&
	    ((NmStat = NmUnMarshalLength(ppBuf, &(pImpHdr->HdrLen))) ==
		NMSTAT_SUCCESS) &&
	    ((NmStat = NmUnMarshalU16(ppBuf, &(pImpHdr->OpCode))) ==
		NMSTAT_SUCCESS) &&
	    ((NmStat = NmUnMarshalStatusCode(ppBuf, &(pImpHdr->StatCode))) ==
		NMSTAT_SUCCESS) &&
	    ((NmStat = NmUnMarshalObjectId(ppBuf, &(pImpHdr->ObjId))) ==
		NMSTAT_SUCCESS) &&
	    ((NmStat = NmUnMarshalUserId(ppBuf, &(pImpHdr->Uid))) ==
		NMSTAT_SUCCESS) &&
	    ((NmStat = NmUnMarshalU32(ppBuf, &(pImpHdr->Seq))) ==
		NMSTAT_SUCCESS) &&
	    ((NmStat = NmUnMarshalU32(ppBuf, &(pImpHdr->TransId1))) ==
		NMSTAT_SUCCESS) &&
	    ((NmStat = NmUnMarshalU32(ppBuf, &(pImpHdr->TransId2))) ==
		NMSTAT_SUCCESS) &&
	    ((NmStat = NmUnMarshalU16(ppBuf, &(pImpHdr->Access))) ==
		NMSTAT_SUCCESS) &&
	    ((NmStat = NmUnMarshalU16(ppBuf, &(pImpHdr->Link))) ==
		NMSTAT_SUCCESS))
	{
		NmStat = NmUnMarshalAppAddr(ppBuf, pImpHdr->AppAddr,
			&(pImpHdr->AppAddrLen), IMP_APP_ADDR_BUFFER_LEN);
	}
	return (NmStat);
}

tNMSTAT NmUnMarshalImpHeadHead(u8** ppBuf, tIMPHDR* pImpHdr)
{
	tNMSTAT	NmStat;

	bzero((char*)pImpHdr, sizeof(tIMPHDR));
	if (((NmStat = NmUnMarshalLength(ppBuf, &(pImpHdr->MsgLen))) ==
		NMSTAT_SUCCESS) &&
	    ((NmStat = NmUnMarshalLength(ppBuf, &(pImpHdr->HdrLen))) ==
		NMSTAT_SUCCESS) &&
	    ((NmStat = NmUnMarshalU16(ppBuf, &(pImpHdr->OpCode))) ==
		NMSTAT_SUCCESS) &&
	    ((NmStat = NmUnMarshalStatusCode(ppBuf, &(pImpHdr->StatCode))) ==
		NMSTAT_SUCCESS))
	{
		NmStat = NmUnMarshalObjectId(ppBuf, &(pImpHdr->ObjId));
	}
	return (NmStat);
}




tNMSTAT NmMarshalEventHeader(u8** ppBuf, tEVHDR* pEvHdr, tNMLEN BodyLen)
{
	tNMSTAT	NmStat;
	u8*	pMsgLen;
	u8*	pHdrLen;
	tNMLEN	MsgLen;
	tNMLEN	HdrLen;
	u8	EvCode;


	/* Note: The prolog of the event header must always match that of the
	 * IMP message header.
	 */
	pMsgLen = *ppBuf;
	EvCode = pEvHdr->EvCode;
	if ((NmStat = NmMarshalLength(ppBuf, 1000)) == NMSTAT_SUCCESS)
	{
		pHdrLen = *ppBuf;
		if (((NmStat = NmMarshalLength(ppBuf, 1000) ==
			NMSTAT_SUCCESS)) &&
		    ((NmStat = NmMarshalU16(ppBuf, (u16)IMP_MSG_EVENT_IND)) ==
			NMSTAT_SUCCESS) &&
		    ((NmStat = NmMarshalObjectId(ppBuf, &(pEvHdr->ObjId))) ==
			NMSTAT_SUCCESS) &&
		    ((NmStat = NmMarshalEventId(ppBuf, pEvHdr->EvCls,
			pEvHdr->EvNum)) == NMSTAT_SUCCESS) &&
		    ((NmStat = NmMarshalU8(ppBuf, EvCode)) ==
			NMSTAT_SUCCESS) &&
		    ((NmStat = NmMarshalU32(ppBuf, pEvHdr->Time)) ==
			NMSTAT_SUCCESS) &&
		    ((NmStat = NmMarshalU32(ppBuf, pEvHdr->GlEvNum)) ==
			NMSTAT_SUCCESS))
		{
			if (EvCode == IMP_EVENT_ALARM)
			{
				/* Alarm record adds time of first
				 * occurrence and counter.
				 */
				if ((NmStat = NmMarshalU32(ppBuf,
					pEvHdr->Time1)) ==
					NMSTAT_SUCCESS)
				{
					NmStat = NmMarshalU32(ppBuf,
						pEvHdr->Count);
				}
			}
			else if (EvCode == IMP_EVENT_REPLY)
			{
				/* Reply record adds action id,
				 * sequence, transaction ids, and
				 * return address (from action request).
				 */
				if (((NmStat = NmMarshalActionId(ppBuf,
					pEvHdr->ActCls, pEvHdr->ActNum))
					== NMSTAT_SUCCESS) &&
				    ((NmStat = NmMarshalU32(ppBuf,
					pEvHdr->Seq)) ==
					NMSTAT_SUCCESS) &&
				    ((NmStat = NmMarshalU32(ppBuf,
					pEvHdr->TransId1)) ==
					NMSTAT_SUCCESS) &&
				    ((NmStat = NmMarshalU32(ppBuf,
					pEvHdr->TransId2)) ==
					NMSTAT_SUCCESS))
				{
					NmStat = NmMarshalAppAddr(ppBuf,
						pEvHdr->AppAddr,
						pEvHdr->AppAddrLen);
				}
			}
			if (NmStat == NMSTAT_SUCCESS)
			{
				HdrLen = (tNMLEN)(*ppBuf - pMsgLen);
				MsgLen = HdrLen + BodyLen;
				if (((NmStat = NmMarshalLength(&pMsgLen,
					MsgLen)) == NMSTAT_SUCCESS) &&
				    ((NmStat = NmMarshalLength(&pHdrLen,
					HdrLen)) == NMSTAT_SUCCESS))
				{
					/* Update the caller's event header
					 * structure.
					 */
					pEvHdr->MsgLen = MsgLen;
					pEvHdr->HdrLen = HdrLen;
				}
			}
		}
	}
	return (NmStat);
}

tNMSTAT NmUnMarshalEventHeader(u8** ppBuf, tEVHDR* pEvHdr)
{
	tNMSTAT	NmStat;
	u8	EvCode;

	bzero((char*)pEvHdr, sizeof(tEVHDR));
	if (((NmStat = NmUnMarshalLength(ppBuf, &(pEvHdr->MsgLen))) ==
		NMSTAT_SUCCESS) &&
	    ((NmStat = NmUnMarshalLength(ppBuf, &(pEvHdr->HdrLen))) ==
		NMSTAT_SUCCESS) &&
	    ((NmStat = NmUnMarshalU16(ppBuf, &(pEvHdr->OpCode))) ==
		NMSTAT_SUCCESS) &&
	    ((NmStat = NmUnMarshalObjectId(ppBuf, &(pEvHdr->ObjId))) ==
		NMSTAT_SUCCESS) &&
	    ((NmStat = NmUnMarshalEventId(ppBuf, &(pEvHdr->EvCls),
		&(pEvHdr->EvNum))) == NMSTAT_SUCCESS) &&
	    ((NmStat = NmUnMarshalU8(ppBuf, &(pEvHdr->EvCode))) ==
		NMSTAT_SUCCESS) &&
	    ((NmStat = NmUnMarshalU32(ppBuf, &(pEvHdr->Time))) ==
		NMSTAT_SUCCESS) &&
	    ((NmStat = NmUnMarshalU32(ppBuf, &(pEvHdr->GlEvNum))) ==
		NMSTAT_SUCCESS))
	{
		EvCode = pEvHdr->EvCode;
		if (EvCode == IMP_EVENT_ALARM)
		{
			/* Alarm record adds time of first occurrence and
			 * counter.
			 */
			if ((NmStat = NmUnMarshalU32(ppBuf, &(pEvHdr->Time1)))
				== NMSTAT_SUCCESS)
			{
				NmStat = NmUnMarshalU32(ppBuf,
					&(pEvHdr->Count));
			}
		}
		else if (EvCode == IMP_EVENT_REPLY)
		{
			/* Reply record adds action id, sequence, transaction
			 * ids, and return address (from action request).
			 */
			if (((NmStat = NmUnMarshalActionId(ppBuf,
				&(pEvHdr->ActCls), &(pEvHdr->ActNum)))
				== NMSTAT_SUCCESS) &&
			    ((NmStat = NmUnMarshalU32(ppBuf, &(pEvHdr->Seq)))
				== NMSTAT_SUCCESS) &&
			    ((NmStat = NmUnMarshalU32(ppBuf,
				&(pEvHdr->TransId1))) == NMSTAT_SUCCESS) &&
			    ((NmStat = NmUnMarshalU32(ppBuf,
				&(pEvHdr->TransId2))) == NMSTAT_SUCCESS))
			{
				NmStat = NmUnMarshalAppAddr(ppBuf,
					pEvHdr->AppAddr, &(pEvHdr->AppAddrLen),
					IMP_APP_ADDR_BUFFER_LEN);
			}
		}
	}
	return (NmStat);
}

tNMSTAT NmReadImpOpCode(u8* pBuf, u16* pOpCode)
{
	u8*	pOpBytes;

	pOpBytes = pBuf + (2 * IMP_TYPE_LENGTH_MBYTES);
	return (NmUnMarshalU16(&pOpBytes, pOpCode));
}

tNMSTAT NmWriteImpOpCode(u8* pBuf, u16 OpCode)
{
	tNMSTAT	NmStat;
	u8*	pDst;

	pDst = pBuf + (2 * IMP_TYPE_LENGTH_MBYTES);
	NmStat = NmMarshalU16(&pDst, OpCode);
	return (NmStat);
}

tNMSTAT NmWriteImpOpAndStatus(u8* pBuf, u16 OpCode, u16 StatusCode)
{
	tNMSTAT	NmStat;
	u8*	pDst;

	pDst = pBuf + (2 * IMP_TYPE_LENGTH_MBYTES);
	NmStat = NmMarshalU16(&pDst, OpCode);
	if (NmStat == NMSTAT_SUCCESS)
	{
		NmStat = NmMarshalStatusCode(&pDst, StatusCode);
	}
	return (NmStat);
}

/* NmSkipImpField advances the buffer pointer past the current IMP field.
 * Notice that it does no error checking and instead fails miserably if there
 * is a protocol violation.
 */
void NmSkipImpField(u8** ppBuf)
{
	u8*	pSrc;
	u8	Tag;
	u8	UnitSize;
	u16	Length;

	pSrc = *ppBuf;
	Tag = *pSrc++;
	UnitSize = (Tag & IMP_UNIT_MASK) + 1;
	if (Tag & IMP_ARRAY_MASK)
	{
		Length = *pSrc++;
		Length <<= 8;
		Length |= *pSrc++;
	}
	else
	{
		Length = 1;
	}
	pSrc += (Length * UnitSize);
	*ppBuf = pSrc;
}

/* NmCopyImpField copies the current IMP field to the specified destination,
 * advancing the buffer pointers as well.  Notice that it does no error
 * checking and instead fails miserably if there is a protocol violation.
 */
void NmCopyImpField(u8** ppBuf, u8** ppDst)
{
	u8*	pSrc;
	u8*	pData;
	u8*	pDst;
	u8	Tag;
	u8	UnitSize;
	u16	Length;

	pSrc = *ppBuf;
	pData = pSrc;
	Tag = *pSrc++;
	UnitSize = (Tag & IMP_UNIT_MASK) + 1;
	if (Tag & IMP_ARRAY_MASK)
	{
		Length = *pSrc++;
		Length <<= 8;
		Length |= *pSrc++;
	}
	else
	{
		Length = 1;
	}
	pSrc += (Length * UnitSize);
	pDst = *ppDst;
	do
	{
		*pDst++ = *pData;
	} while (++pData < pSrc);
	*ppBuf = pSrc;
	*ppDst = pDst;
}

/* NmSetAttrStatus scans for the indicated attribute id and, if it's found,
 * sets the status to the value specified.  It is assumed that the buffer has
 * the format <attr id> <value> <status>, as in a set response.  This function
 * is called from the various nmc-generated "reject" functions.
 */
tNMSTAT NmSetAttrStatus(u8* pBuf, tCID Cid, u8 AttrId, tNMSTAT AttrStat)
{
	tNMSTAT	NmStat;
	bool	FoundIt;
	u8*	pSrc;
	u8*	pMark;
	tCID	MsgCid;
	u8	MsgAttrId;
	tNMSTAT	MsgStat;

	NmStat = NMSTAT_SUCCESS;
	FoundIt = FALSE;
	pSrc = pBuf;
	while ((*pSrc & IMP_ARRAY_TYPE_MASK) != IMP_TYPE_END_OF_MSG)
	{
		if ((NmStat = NmUnMarshalAttrId(&pSrc, &MsgCid, &MsgAttrId)) !=
			NMSTAT_SUCCESS)
		{
			break;
		}
		NmSkipImpField(&pSrc); /* Eat the value. */
		pMark = pSrc;
		if ((NmStat = NmUnMarshalStatusCode(&pSrc, &MsgStat)) !=
			NMSTAT_SUCCESS)
		{
			break;
		}
		if ((MsgCid == Cid) && (MsgAttrId == AttrId))
		{
			*(pMark + 1) = (u8)(AttrStat >> 8);
			*(pMark + 2) = (u8)(AttrStat);
			FoundIt = TRUE;
			break;
		}
	}
	if ((NmStat == NMSTAT_SUCCESS) && (!FoundIt))
	{
		NmStat = NMERROR_INTERNAL_ATTR;
	}
	return (NmStat);
}

/* NmMarshalByteArIx marshals an array of index and one-byte value pairs
 * (tARu8).  It is used to set specific array elements.
 */
tNMSTAT NmMarshalByteArIx(u8** ppBuf, tARu8* pArIx, u16 ArIxLen, u8 Tag)
{
	tNMSTAT	NmStat = NMSTAT_SUCCESS;
	u8*	pDst;
	u8*	pLen;
	tARu8*	pSrc;
	tARu8*	pSrcEnd;
	u16	ElemCount;
	u16	Length;
	u8	Index;

	ElemCount = 0;
	pDst = *ppBuf;
	*pDst++ = Tag;
	pLen = pDst;	/* remember where to put length */
	pDst += 2;	/* bump past length */
	pSrc = pArIx;
	pSrcEnd = pSrc + ArIxLen;
	if (pSrc < pSrcEnd) do
	{
		Index = pSrc->Index;
		if (Index < MAX_ARRAY1_LENGTH)
		{
			*pDst++ = Index;
			*pDst++ = pSrc->Value;
			++ElemCount;
		}
		else
		{
			NmStat = NMERROR_ARRAY_TOO_BIG;
		}
	} while (++pSrc < pSrcEnd);
	/*
	 * IMP array length value is 2 * number of index/value pairs, since
	 * unit is 1 byte and both index and value are 1 byte long.
	 */
	Length = ElemCount << 1;
	*pLen++ = (u8)(Length >> 8);
	*pLen = (u8)Length;
	*ppBuf = pDst;
	return (NmStat);
}

/* NmUnMarshalARu8 unmarshals a sequence of index/value pairs into an array of
 * index and one-byte value pairs (tARu8).
 */
tNMSTAT NmUnMarshalARu8(u8** ppBuf, tARu8* pArIx, u16* pArIxLen, u16 BufLen)
{
	tNMSTAT	NmStat;
	u8*	pSrc;
	u8*	pSrcEnd;
	tARu8*	pAr;
	tARu8*	pArEnd;
	u16	Length;
	u8	Tag;
	u8	Type;
	u8	Unit;
	ix	Offset;

	pSrc = *ppBuf;
	Tag = *pSrc++;
	Type = Tag & IMP_ARRAY_TYPE_MASK;
	if ((Type == IMP_ARRAY_IX_UINT) || (Type == IMP_ARRAY_IX_SINT))
	{
		NmStat = NMSTAT_SUCCESS;
		Unit = Tag & IMP_UNIT_MASK;
		Length = *pSrc++;
		Length <<= 8;
		Length |= *pSrc++;
		pSrcEnd = pSrc + ((Length - 1) * (Unit + 1));
		pAr = pArIx;
		pArEnd = pAr + BufLen;
		Offset = (ix)Unit;
		if ((pAr < pArEnd) && (pSrc < pSrcEnd)) do
		{
			pAr->Index = *pSrc++;
			/*
			 * Adjust source to discard most significant
			 * bytes (if any).
			 */
			pSrc += Offset;
			pAr->Value = *pSrc++;
		} while ((++pAr < pArEnd) && (pSrc < pSrcEnd));
		if (pSrc < pSrcEnd)
		{
			NmStat = NMERROR_ARRAY_MISMATCH;
		}
		*ppBuf = pSrcEnd + (Unit + 1);
		*pArIxLen = (u16)(pAr - pArIx);
	}
	else
	{
		NmStat = NMERROR_PROTOCOL;
		*pArIxLen = 0;
	}
	return (NmStat);
}

/* NmUnMarshalArrayIxU8 unmarshals a sequence of index/value pairs directly
 * into an array of one-byte values (u8).
 */
tNMSTAT NmUnMarshalArrayIxU8(u8** ppBuf, u8* pArray, u16* pArrayLen, u16 BufLen)
{
	tNMSTAT	NmStat;
	u8*	pSrc;
	u8*	pSrcEnd;
	u8*	pArrayEnd;
	u16	Length;
	u8	Tag;
	u8	Type;
	u8	Unit;
	ix	Offset;
	u8	Index;
	u8	Value;
	u16	MaxIndex;

	MaxIndex = 0;
	pSrc = *ppBuf;
	Tag = *pSrc++;
	Type = Tag & IMP_ARRAY_TYPE_MASK;
	if ((Type == IMP_ARRAY_IX_UINT) || (Type == IMP_ARRAY_IX_SINT))
	{
		NmStat = NMSTAT_SUCCESS;
		Unit = Tag & IMP_UNIT_MASK;
		Length = *pSrc++;
		Length <<= 8;
		Length |= *pSrc++;
		pSrcEnd = pSrc + ((Length - 1) * (Unit + 1));
		Offset = (ix)Unit;
		pArrayEnd = pArray + BufLen;
		if (pSrc < pSrcEnd) do
		{
			Index = *pSrc++;
			if (Index > MaxIndex)
			{
				MaxIndex = Index;
			}
			/*
			 * Adjust source to discard most significant
			 * bytes (if any).
			 */
			pSrc += Offset;
			Value = *pSrc++;
			if (Index < BufLen)
			{
				*(pArray + Index) = Value;
			}
			else
			{
				NmStat = NMERROR_ARRAY_TOO_BIG;
			}
		} while (pSrc < pSrcEnd);
		*ppBuf = pSrcEnd + (Unit + 1);
	}
	else
	{
		NmStat = NMERROR_PROTOCOL;
	}
	*pArrayLen = MaxIndex;
	return (NmStat);
}

/* NmMarshalWordArIx marshals an array of index and two-byte value pairs
 * (tARu16).  It is used to set specific array elements.
 */
tNMSTAT NmMarshalWordArIx(u8** ppBuf, tARu16* pArIx, u16 ArIxLen, u8 Tag)
{
	tNMSTAT	NmStat = NMSTAT_SUCCESS;
	u8*	pDst;
	u8*	pLen;
	tARu16*	pSrc;
	tARu16*	pSrcEnd;
	u16	ElemCount;
	u16	Length;
	u8	Index;
	u16	Value;

	ElemCount = 0;
	pDst = *ppBuf;
	*pDst++ = Tag;
	pLen = pDst;	/* remember where to put length */
	pDst += 2;	/* bump past length */
	pSrc = pArIx;
	pSrcEnd = pSrc + ArIxLen;
	if (pSrc < pSrcEnd) do
	{
		Index = pSrc->Index;
		if (Index < MAX_ARRAY1_LENGTH)
		{
			*pDst++ = Index;
			Value = pSrc->Value;
			*pDst++ = (u8)(Value >> 8);
			*pDst++ = (u8)Value;
			++ElemCount;
		}
		else
		{
			NmStat = NMERROR_ARRAY_TOO_BIG;
		}
	} while (++pSrc < pSrcEnd);
	/*
	 * IMP array length value is 1/2 number of indexes + number of values,
	 * since unit is 2 bytes and index is 1 byte long and value is 2 bytes
	 * long.  If the number of elements is odd, a pad byte is put at the
	 * end of the IMP array.
	 */
	Length = ((ElemCount + 1) >> 1) + ElemCount;
	*pLen++ = (u8)(Length >> 8);
	*pLen = (u8)Length;
	if (ElemCount & 1)
	{
		*pDst++ = (u8)0;
	}
	*ppBuf = pDst;
	return (NmStat);
}

/* NmUnMarshalARu16 unmarshals a sequence of index/value pairs into an array of
 * index and two-byte value pairs (tARu16).
 */
tNMSTAT NmUnMarshalARu16(u8** ppBuf, tARu16* pArIx, u16* pArIxLen, u16 BufLen)
{
	tNMSTAT	NmStat;
	u8*	pSrc;
	u8*	pSrcEnd;
	tARu16*	pAr;
	tARu16*	pArEnd;
	u16	Length;
	u8	Tag;
	u8	Type;
	u8	Unit;
	ix	Offset;
	u16	Value;

	pSrc = *ppBuf;
	Tag = *pSrc++;
	Type = Tag & IMP_ARRAY_TYPE_MASK;
	if ((Type == IMP_ARRAY_IX_UINT) || (Type == IMP_ARRAY_IX_SINT))
	{
		NmStat = NMSTAT_SUCCESS;
		Unit = Tag & IMP_UNIT_MASK;
		Length = *pSrc++;
		Length <<= 8;
		Length |= *pSrc++;
		pSrcEnd = pSrc + ((Length - 1) * (Unit + 1));
		pAr = pArIx;
		pArEnd = pAr + BufLen;
		if (Unit == IMP_UNIT_1)
		{
			if ((pAr < pArEnd) && (pSrc < pSrcEnd)) do
			{
				pAr->Index = *pSrc++;
				Value = (u16)(*pSrc++);
				if ((Type == IMP_ARRAY_IX_SINT) &&
				    (Value & (u16)0x080))
				{
					/* Sign extension.
					 */
					Value |= (u16)0x0ff00;
				}
				pAr->Value = Value;
			} while ((++pAr < pArEnd) && (pSrc < pSrcEnd));
		}
		else
		{
			Offset = (ix)(Unit - 1);
			if ((pAr < pArEnd) && (pSrc < pSrcEnd)) do
			{
				pAr->Index = *pSrc++;
				/*
				 * Adjust source to discard most significant
				 * bytes (if any).
				 */
				pSrc += Offset;
				Value = (u16)((*pSrc++) << 8);
				Value |= (u16)(*pSrc++);
				pAr->Value = Value;
			} while ((++pAr < pArEnd) && (pSrc < pSrcEnd));
		}
		if (pSrc < pSrcEnd)
		{
			NmStat = NMERROR_ARRAY_MISMATCH;
		}
		*ppBuf = pSrcEnd + (Unit + 1);
		*pArIxLen = (u16)(pAr - pArIx);
	}
	else
	{
		NmStat = NMERROR_PROTOCOL;
		*pArIxLen = 0;
	}
	return (NmStat);
}

/* NmUnMarshalArrayIxU16 unmarshals a sequence of index/value pairs directly
 * into an array of two-byte values (u16).
 */
tNMSTAT NmUnMarshalArrayIxU16(u8** ppBuf, u16* pArray, u16* pArrayLen,
	u16 BufLen)
{
	tNMSTAT	NmStat;
	u8*	pSrc;
	u8*	pSrcEnd;
	u16*	pArrayEnd;
	u16	Length;
	u8	Tag;
	u8	Type;
	u8	Unit;
	ix	Offset;
	u8	Index;
	u16	Value;
	u16	MaxIndex;

	MaxIndex = 0;
	pSrc = *ppBuf;
	Tag = *pSrc++;
	Type = Tag & IMP_ARRAY_TYPE_MASK;
	if ((Type == IMP_ARRAY_IX_UINT) || (Type == IMP_ARRAY_IX_SINT))
	{
		NmStat = NMSTAT_SUCCESS;
		Unit = Tag & IMP_UNIT_MASK;
		Length = *pSrc++;
		Length <<= 8;
		Length |= *pSrc++;
		pSrcEnd = pSrc + ((Length - 1) * (Unit + 1));
		pArrayEnd = pArray + BufLen;
		if (Unit == IMP_UNIT_1)
		{
			if (pSrc < pSrcEnd) do
			{
				Index = *pSrc++;
				if (Index > MaxIndex)
				{
					MaxIndex = Index;
				}
				Value = (u16)(*pSrc++);
				if (Index < BufLen)
				{
					if ((Type == IMP_ARRAY_IX_SINT) &&
					    (Value & (u16)0x080))
					{
						/* Sign extension.
						 */
						Value |= (u16)0x0ff00;
					}
					*(pArray + Index) = Value;
				}
				else
				{
					NmStat = NMERROR_ARRAY_TOO_BIG;
				}
			} while (pSrc < pSrcEnd);
		}
		else
		{
			Offset = (ix)(Unit - 1);
			if (pSrc < pSrcEnd) do
			{
				Index = *pSrc++;
				if (Index > MaxIndex)
				{
					MaxIndex = Index;
				}
				/*
				 * Adjust source to discard most significant
				 * bytes (if any).
				 */
				pSrc += Offset;
				Value = (u16)((*pSrc++) << 8);
				Value |= (u16)(*pSrc++);
				if (Index < BufLen)
				{
					*(pArray + Index) = Value;
				}
				else
				{
					NmStat = NMERROR_ARRAY_TOO_BIG;
				}
			} while (pSrc < pSrcEnd);
		}
		*ppBuf = pSrcEnd + (Unit + 1);
	}
	else
	{
		NmStat = NMERROR_PROTOCOL;
	}
	*pArrayLen = MaxIndex;
	return (NmStat);
}

/* NmMarshalDWordArIx marshals an array of index and four-byte value pairs
 * (tARu32).  It is used to set specific array elements.
 */
tNMSTAT NmMarshalDWordArIx(u8** ppBuf, tARu32* pArIx, u16 ArIxLen, u8 Tag)
{
	tNMSTAT	NmStat = NMSTAT_SUCCESS;
	u8*	pDst;
	u8*	pLen;
	tARu32*	pSrc;
	tARu32*	pSrcEnd;
	u16	ElemCount;
	u16	Length;
	u8	Index;
	u32	Value;

	ElemCount = 0;
	pDst = *ppBuf;
	*pDst++ = Tag;
	pLen = pDst;	/* remember where to put length */
	pDst += 2;	/* bump past length */
	pSrc = pArIx;
	pSrcEnd = pSrc + ArIxLen;
	if (pSrc < pSrcEnd) do
	{
		Index = pSrc->Index;
		if (Index < MAX_ARRAY1_LENGTH)
		{
			*pDst++ = Index;
			Value = pSrc->Value;
			*pDst++ = (u8)(Value >> 24);
			*pDst++ = (u8)(Value >> 16);
			*pDst++ = (u8)(Value >> 8);
			*pDst++ = (u8)Value;
			++ElemCount;
		}
		else
		{
			NmStat = NMERROR_ARRAY_TOO_BIG;
		}
	} while (++pSrc < pSrcEnd);
	/*
	 * IMP array length value is 1/4 number of indexes + number of values,
	 * since unit is 4 bytes and index is 1 byte long and value is 4 bytes
	 * long.  If the number of elements is not a multiple of four, up to
	 * three pad bytes are put at the end of the IMP array.
	 */
	Length = ((ElemCount + 3) >> 2) + ElemCount;
	*pLen++ = (u8)(Length >> 8);
	*pLen = (u8)Length;
	while (ElemCount++ & 3)
	{
		*pDst++ = (u8)0;
	}
	*ppBuf = pDst;
	return (NmStat);
}

/* NmUnMarshalARu32 unmarshals a sequence of index/value pairs into an array of
 * index and four-byte value pairs (tARu32).
 */
tNMSTAT NmUnMarshalARu32(u8** ppBuf, tARu32* pArIx, u16* pArIxLen, u16 BufLen)
{
	tNMSTAT	NmStat;
	u8*	pSrc;
	u8*	pSrcEnd;
	tARu32*	pAr;
	tARu32*	pArEnd;
	u16	Length;
	u8	Tag;
	u8	Type;
	u8	Unit;
	u32	Value;

	pSrc = *ppBuf;
	Tag = *pSrc++;
	Type = Tag & IMP_ARRAY_TYPE_MASK;
	if ((Type == IMP_ARRAY_IX_UINT) || (Type == IMP_ARRAY_IX_SINT))
	{
		NmStat = NMSTAT_SUCCESS;
		Unit = Tag & IMP_UNIT_MASK;
		Length = *pSrc++;
		Length <<= 8;
		Length |= *pSrc++;
		pSrcEnd = pSrc + ((Length - 1) * (Unit + 1));
		pAr = pArIx;
		pArEnd = pAr + BufLen;
		if ((pAr < pArEnd) && (pSrc < pSrcEnd)) do
		{
			pAr->Index = *pSrc++;
			switch (Unit)
			{
			  case IMP_UNIT_4:
				Value = (u32)(*pSrc++);
				Value <<= 8;
				Value |= (u32)(*pSrc++);
				Value <<= 8;
				Value |= (u32)(*pSrc++);
				Value <<= 8;
				Value |= (u32)(*pSrc++);
				break;
			  case IMP_UNIT_3:
				Value = (u32)(*pSrc++);
				Value <<= 8;
				Value |= (u32)(*pSrc++);
				Value <<= 8;
				Value |= (u32)(*pSrc++);
				if ((Type == IMP_ARRAY_IX_SINT) &&
				    (Value & (u32)0x0800000))
				{
					/* Sign extension.
					 */
					Value |= (u32)0x0ff000000;
				}
				break;
			  case IMP_UNIT_2:
				Value = (u32)(*pSrc++);
				Value <<= 8;
				Value |= (u32)(*pSrc++);
				if ((Type == IMP_ARRAY_IX_SINT) &&
				    (Value & (u32)0x08000))
				{
					/* Sign extension.
					 */
					Value |= (u32)0x0ffff0000;
				}
				break;
			  case IMP_UNIT_1:
				Value = (u32)(*pSrc++);
				if ((Type == IMP_ARRAY_IX_SINT) &&
				    (Value & (u32)0x080))
				{
					/* Sign extension.
					 */
					Value |= (u32)0x0ffffff00;
				}
				break;
			  default:
				Value = 0;
				break;
			}
			pAr->Value = Value;
		} while ((++pAr < pArEnd) && (pSrc < pSrcEnd));
		if (pSrc < pSrcEnd)
		{
			NmStat = NMERROR_ARRAY_MISMATCH;
		}
		*ppBuf = pSrcEnd + (Unit + 1);
		*pArIxLen = (u16)(pAr - pArIx);
	}
	else
	{
		NmStat = NMERROR_PROTOCOL;
		*pArIxLen = 0;
	}
	return (NmStat);
}

/* NmUnMarshalArrayIxU32 unmarshals a sequence of index/value pairs directly
 * into an array of four-byte values (u32).
 */
tNMSTAT NmUnMarshalArrayIxU32(u8** ppBuf, u32* pArray, u16* pArrayLen,
	u16 BufLen)
{
	tNMSTAT	NmStat;
	u8*	pSrc;
	u8*	pSrcEnd;
	u32*	pArrayEnd;
	u16	Length;
	u8	Tag;
	u8	Type;
	u8	Unit;
	u8	Index;
	u32	Value;
	u16	MaxIndex;

	MaxIndex = 0;
	pSrc = *ppBuf;
	Tag = *pSrc++;
	Type = Tag & IMP_ARRAY_TYPE_MASK;
	if ((Type == IMP_ARRAY_IX_UINT) || (Type == IMP_ARRAY_IX_SINT))
	{
		NmStat = NMSTAT_SUCCESS;
		Unit = Tag & IMP_UNIT_MASK;
		Length = *pSrc++;
		Length <<= 8;
		Length |= *pSrc++;
		pSrcEnd = pSrc + ((Length - 1) * (Unit + 1));
		pArrayEnd = pArray + BufLen;
		if (pSrc < pSrcEnd) do
		{
			Index = *pSrc++;
			if (Index > MaxIndex)
			{
				MaxIndex = Index;
			}
			switch (Unit)
			{
			  case IMP_UNIT_4:
				Value = (u32)(*pSrc++);
				Value <<= 8;
				Value |= (u32)(*pSrc++);
				Value <<= 8;
				Value |= (u32)(*pSrc++);
				Value <<= 8;
				Value |= (u32)(*pSrc++);
				break;
			  case IMP_UNIT_3:
				Value = (u32)(*pSrc++);
				Value <<= 8;
				Value |= (u32)(*pSrc++);
				Value <<= 8;
				Value |= (u32)(*pSrc++);
				if ((Type == IMP_ARRAY_IX_SINT) &&
				    (Value & (u32)0x0800000))
				{
					/* Sign extension.
					 */
					Value |= (u32)0x0ff000000;
				}
				break;
			  case IMP_UNIT_2:
				Value = (u32)(*pSrc++);
				Value <<= 8;
				Value |= (u32)(*pSrc++);
				if ((Type == IMP_ARRAY_IX_SINT) &&
				    (Value & (u32)0x08000))
				{
					/* Sign extension.
					 */
					Value |= (u32)0x0ffff0000;
				}
				break;
			  case IMP_UNIT_1:
				Value = (u32)(*pSrc++);
				if ((Type == IMP_ARRAY_IX_SINT) &&
				    (Value & (u32)0x080))
				{
					/* Sign extension.
					 */
					Value |= (u32)0x0ffffff00;
				}
				break;
			  default:
				Value = 0;
				break;
			}
			if (Index < BufLen)
			{
				*(pArray + Index) = Value;
			}
			else
			{
				NmStat = NMERROR_ARRAY_TOO_BIG;
			}
		} while (pSrc < pSrcEnd);
		*ppBuf = pSrcEnd + (Unit + 1);
	}
	else
	{
		NmStat = NMERROR_PROTOCOL;
	}
	*pArrayLen = MaxIndex;
	return (NmStat);
}

/* NmUnMarshalAnyArU8 will unmarshal either of the u8 array types.
 */
tNMSTAT NmUnMarshalAnyArU8(u8** ppBuf, u8* pArray, u16* pArrayLen, u16 BufLen)
{
	tNMSTAT	NmStat;
	u8	Type;

	Type = **ppBuf & IMP_ARRAY_TYPE_MASK;
	if ((Type == IMP_ARRAY_UINT) || (Type == IMP_ARRAY_SINT))
	{
		NmStat = NmUnMarshalArrayU8(ppBuf, pArray, pArrayLen,
			BufLen);
	}
	else if ((Type == IMP_ARRAY_IX_UINT) || (Type == IMP_ARRAY_IX_SINT))
	{
		NmStat = NmUnMarshalArrayIxU8(ppBuf, pArray, pArrayLen,
			BufLen);
	}
	else
	{
		NmStat = NMERROR_PROTOCOL;
		*pArrayLen = 0;
	}
	return (NmStat);
}

/* NmUnMarshalAnyArU16 will unmarshal either of the u16 array types.
 */
tNMSTAT NmUnMarshalAnyArU16(u8** ppBuf, u16* pArray, u16* pArrayLen, u16 BufLen)
{
	tNMSTAT	NmStat;
	u8	Type;

	Type = **ppBuf & IMP_ARRAY_TYPE_MASK;
	if ((Type == IMP_ARRAY_UINT) || (Type == IMP_ARRAY_SINT))
	{
		NmStat = NmUnMarshalArrayU16(ppBuf, pArray, pArrayLen,
			BufLen);
	}
	else if ((Type == IMP_ARRAY_IX_UINT) || (Type == IMP_ARRAY_IX_SINT))
	{
		NmStat = NmUnMarshalArrayIxU16(ppBuf, pArray, pArrayLen,
			BufLen);
	}
	else
	{
		NmStat = NMERROR_PROTOCOL;
		*pArrayLen = 0;
	}
	return (NmStat);
}

/* NmUnMarshalAnyArU32 will unmarshal either of the u32 array types.
 */
tNMSTAT NmUnMarshalAnyArU32(u8** ppBuf, u32* pArray, u16* pArrayLen, u16 BufLen)
{
	tNMSTAT	NmStat;
	u8	Type;

	Type = **ppBuf & IMP_ARRAY_TYPE_MASK;
	if ((Type == IMP_ARRAY_UINT) || (Type == IMP_ARRAY_SINT))
	{
		NmStat = NmUnMarshalArrayU32(ppBuf, pArray, pArrayLen,
			BufLen);
	}
	else if ((Type == IMP_ARRAY_IX_UINT) || (Type == IMP_ARRAY_IX_SINT))
	{
		NmStat = NmUnMarshalArrayIxU32(ppBuf, pArray, pArrayLen,
			BufLen);
	}
	else
	{
		NmStat = NMERROR_PROTOCOL;
		*pArrayLen = 0;
	}
	return (NmStat);
}
