本文剖析asn-int.h/c,從源代碼來學(xué)習(xí)eSNACC對INTEGER的編碼和解碼。
eSNACC中的整形與上一篇所講的布爾型一樣,也很簡單,不過代碼中還是有一點有趣的地方。
eSNACC定義的整形AsnInt是用4字節(jié)的整數(shù)表示的。如下代碼
#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
其中用了一段預(yù)處理指令來取得當(dāng)前機器或編譯器的4字節(jié)類型(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 */
這些代碼最神奇的地方就是專門設(shè)計了這樣一個標(biāo)志
#define INT_MASK (0x7f80 << ((sizeof(AsnInt) - 2) * 8))
可以說,一切精華都在這里面。
目的:在實際中,整形是一個4字節(jié)數(shù)來表示的,但是在編碼傳輸時,我們希望盡最大可能來壓縮這個數(shù),也就是取得這個數(shù)的最多有效字節(jié)。
然后就可以分情況討論了:
1.若值data<0:那么其最高位一定為1,那么我們就是要最大限度的壓縮從最高位開始的bit位為1的字節(jié)。只要這些bit位為1,我們就可以壓縮掉了。這也就是以下代碼的目的:
for (len = sizeof (AsnInt); len > 1; --len)

{
if ((dataCpy & mask) == mask)
mask >>= 8;
else
break;
}
2.若值data>0:那么其最高位一定為0,那么我們就是要最大限度的壓縮從最高位開始的bit位為0的字節(jié)。只要這些bit位為0,我們就可以壓縮掉了。這也就是以下代碼的目的:
for (len = sizeof (AsnInt); len > 1; --len)

{
if ((dataCpy & mask) == 0)
mask >>= 8;
else
break;
}
然后在解碼時,就要根據(jù)編碼的這些邏輯來判斷:如果字節(jié)的最高位為1,則說明這個數(shù)是個負數(shù),所以就先將這個字節(jié)的前面填充1擴展。否則,就是正數(shù),不需要變:

if (byte & 0x80) /**//* top bit of first byte is sign bit */
retVal = (-1 << 8) | byte;
else
retVal = byte;
然后在根據(jù)有效字節(jié)長度來擴展還原為本來的值。
for (i = 1; i < (int)len; i++)
retVal = (retVal << 8) | (unsigned long)(BufGetByte (b));
依據(jù)上面的思想,本文件中的其他函數(shù)也就不難理解了。只是在編碼無符號數(shù)時,為了避免混淆,多引入了一個字節(jié)而已。
不過,我感到最值得我們思考和學(xué)習(xí)的是這個mask是如何設(shè)計出來的?我們該怎么思考才能設(shè)計出這么神奇的數(shù)呢?這個還請有經(jīng)驗的朋友總結(jié)和賜教。