本文剖析asn-int.h/c,从源代码来学习eSNACC对INTEGER的编码和解码。
eSNACC中的整形与上一篇所讲的布尔型一样,也很简单,不过代码中还是有一点有趣的地方。
eSNACC定义的整形AsnInt是用4字节的整数表示的。如下代码
#if SIZEOF_INT == 4
# define I int
#else
# if SIZEOF_LONG == 4
# define I long
# else
# if SIZEOF_SHORT == 4
# define I short
# endif
# endif
# endif
#ifdef I
typedef I AsnInt;
typedef unsigned I UAsnInt;
#else
#error "can't find integer type which is 4 bytes in size"
#endif
#undef I
其中用了一段预处理指令来取得当前机器或编译器的4字节类型(int,long or short)。同时定义了无符号的整形UAsnInt。
我们着重研究一下.c文件中的编码和解码。
/**//*
* encodes signed long integer's contents
*/
AsnLen
BEncAsnIntContent PARAMS ((b, data),
GenBuf *b _AND_
AsnInt *data)
{
int len;
int i;
unsigned long mask;
long dataCpy;
#define INT_MASK (0x7f80 << ((sizeof(AsnInt) - 2) * 8))
dataCpy = *data;
/**//*
* calculate encoded length of the integer (content)
*/
mask = INT_MASK;
if (dataCpy < 0)
for (len = sizeof (AsnInt); len > 1; --len)
{
if ((dataCpy & mask) == mask)
mask >>= 8;
else
break;
}
else
for (len = sizeof (AsnInt); len > 1; --len)
{
if ((dataCpy & mask) == 0)
mask >>= 8;
else
break;
}
/**//*
* write the BER integer
*/
for (i = 0; i < len; i++)
{
BufPutByteRvs (b, (unsigned char)dataCpy);
dataCpy >>= 8;
}
return len;
} /**//* BEncAsnIntContent */
/**//*
* Decodes content of BER a INTEGER value. The given tag is ignored.
*/
void
BDecAsnIntContent PARAMS ((b, tagId, len, result, bytesDecoded, env),
GenBuf *b _AND_
AsnTag tagId _AND_
AsnLen len _AND_
AsnInt *result _AND_
AsnLen *bytesDecoded _AND_
jmp_buf env)
{
int i;
long retVal;
unsigned long byte;
if (len > sizeof (AsnInt))
{
Asn1Error ("BDecAsnIntContent: ERROR - integer to big to decode.\n");
longjmp (env, -7);
}
/**//*
* look at integer value
*/
byte = (unsigned long ) BufGetByte (b);
if (byte & 0x80) /**//* top bit of first byte is sign bit */
retVal = (-1 << 8) | byte;
else
retVal = byte;
/**//*
* write from buffer into long int
*/
for (i = 1; i < (int)len; i++)
retVal = (retVal << 8) | (unsigned long)(BufGetByte (b));
if (BufReadError (b))
{
Asn1Error ("BDecAsnIntContent: ERROR - decoded past end of data \n");
longjmp (env, -8);
}
(*bytesDecoded) += len;
*result = retVal;
tagId=tagId; /**//* referenced to avoid compiler warning. */
} /**//* BDecAsnIntContent */
这些代码最神奇的地方就是专门设计了这样一个标志
#define INT_MASK (0x7f80 << ((sizeof(AsnInt) - 2) * 8))
可以说,一切精华都在这里面。
目的:在实际中,整形是一个4字节数来表示的,但是在编码传输时,我们希望尽最大可能来压缩这个数,也就是取得这个数的最多有效字节。
然后就可以分情况讨论了:
1.若值data<0:那么其最高位一定为1,那么我们就是要最大限度的压缩从最高位开始的bit位为1的字节。只要这些bit位为1,我们就可以压缩掉了。这也就是以下代码的目的:
for (len = sizeof (AsnInt); len > 1; --len)
{
if ((dataCpy & mask) == mask)
mask >>= 8;
else
break;
}
2.若值data>0:那么其最高位一定为0,那么我们就是要最大限度的压缩从最高位开始的bit位为0的字节。只要这些bit位为0,我们就可以压缩掉了。这也就是以下代码的目的:
for (len = sizeof (AsnInt); len > 1; --len)
{
if ((dataCpy & mask) == 0)
mask >>= 8;
else
break;
}
然后在解码时,就要根据编码的这些逻辑来判断:如果字节的最高位为1,则说明这个数是个负数,所以就先将这个字节的前面填充1扩展。否则,就是正数,不需要变:
if (byte & 0x80) /**//* top bit of first byte is sign bit */
retVal = (-1 << 8) | byte;
else
retVal = byte;
然后在根据有效字节长度来扩展还原为本来的值。
for (i = 1; i < (int)len; i++)
retVal = (retVal << 8) | (unsigned long)(BufGetByte (b));
依据上面的思想,本文件中的其他函数也就不难理解了。只是在编码无符号数时,为了避免混淆,多引入了一个字节而已。
不过,我感到最值得我们思考和学习的是这个mask是如何设计出来的?我们该怎么思考才能设计出这么神奇的数呢?这个还请有经验的朋友总结和赐教。