eSNACC運行時庫直接支持各種各樣的ASN.1字符串定義。這包括PrintableString, BMPString, TeletexString, NumericString, IA5String, UniversalString, UTF8String, VisibleString。其做法也大同小異,所有這些字符串都是在eSNACC的字節串基礎上typedef過來的,例如:

typedef AsnOcts PrintableString; /**//* [UNIVERSAL 19] IMPLICIT OCTET STRING */
只是根據各自特性的不同而在編碼解碼時有某些判斷,或者加了某些判斷函數等。只有UTF8String相對比較復雜一些。讓我們一起來分析一下吧。
/******************************PrintableString************************************************/
PrintableString也就是可打印字符組成的字符串。因為要求是可以打印的字符,其判斷函數在串中全部為可打印字符的函數時返回0,否則返回-1。
static int chkPrintableString(PrintableString *checkBuf)


{
unsigned int i;
char temp;

if (checkBuf == NULL)
return -1;
for (i = 0; i < checkBuf->octetLen; i++)

{
temp = checkBuf->octs[i];

/**//* Check A-Z */
if ((temp < 'A') || (temp > 'Z'))

{

/**//* Check for a-z */
if ((temp < 'a') || (temp > 'z'))

{

/**//* Check for 0-9 */
if ((temp < '0') || (temp > '9'))

{
switch (temp)

{

case ' ': /**//* space */
case '\'': /* apostrophe */

case '(': /**//* left parenthesis */

case ')': /**//* right parenthesis */

case '+': /**//* plus sign */

case ',': /**//* comma */

case '-': /**//* hyphen */

case '.': /**//* full stop (period) */

case '/': /**//* solidus */

case ':': /**//* colon */

case '=': /**//* equal sign */

case '?': /**//* question mark */
break;

default:
return -1;
}
}
}
}
}

return 0;

} /**//* end of chkPrintableString() */
然后在編碼和解碼時判斷本函數的要求是否滿足,否則報錯。
/******************************************************************************************/
/************************************BMPString**********************************************/
BMPString也就是UNICODE_STRING,也就是雙字節字符。所以要求其內部字節串的長度必須是2的倍數,不然編碼和解碼時就會報錯。
AsnLen BEncBMPStringContent(GenBuf *b, BMPString *octs)


{
if ((octs->octetLen % 2) != 0)

{
BufSetWriteError (b, TRUE);
}
return BEncAsnOctsContent(b, octs);

} /**//* end of BEncBMPStringContent() */

void BDecBMPStringContent(GenBuf *b, AsnTag tagId, AsnLen len,BMPString *result, AsnLen *bytesDecoded,ENV_TYPE env)


{
BDecAsnOctsContent(b, tagId, len, result, bytesDecoded, env);
if ((result->octetLen % 2) != 0)

{
Asn1Error ("BDecBMPStringContent: ERROR - Invalid BMPString Format");
longjmp (env, -40);
}
}
/*****************************************************************************************/
/******************************TeletexString*************************************************/
TeletexString就是8位字節串,所以與AsnOcts是一樣的,只是標簽不同,這也就體現在編碼和解碼時對標簽的處理不同。
/******************************************************************************************/
/******************************NumericString*************************************************/
NumericString就是要求由數字組成的串,不過可以有空格。判斷函數如下:
static int chkNumericString(NumericString *checkBuf)


{
unsigned int i;
if (checkBuf == NULL)
return -1;

for (i = 0; i < checkBuf->octetLen; i++)

{
if ((checkBuf->octs[i] != ' ') &&
((checkBuf->octs[i] < '0') || (checkBuf->octs[i] > '9')))
return -1;
}

return 0;

} /**//* end of chkNumericString() */
/******************************************************************************************/
/******************************IA5String****************************************************/
在微軟的網站上,對IA5的解釋是:The International Alphabet number 5 (IA5) is generally equivalent to the ASCII alphabet, but different versions can include accents or other characters specific to a regional language. eSNACC給的判斷函數是:
static int checkIA5String(IA5String *octs)


{
unsigned int i;

if (octs == NULL)
return -1;

for (i = 0; i < octs->octetLen; i++)

{
if ((unsigned char)octs->octs[i] > 0x7F)
return -1;
}

return 0;
}
/******************************************************************************************/
/******************************UniversalString************************************************/
UniversalString要求是用4個字節表示字符的串。所以在編碼和解碼時要求字節串的長度為4的整數倍:
AsnLen BEncUniversalStringContent(GenBuf *b, UniversalString *octs)


{
if ((octs->octetLen % 4) != 0)

{
Asn1Error ("BEncUniversalStringContent: ERROR - Invalid UniversalString Format");
GenBufSetWriteError (b, TRUE);
}
return BEncAsnOctsContent(b, octs);

} /**//* end of BEncUniversalStringContent() */

void BDecUniversalStringContent(GenBuf *b, AsnTag tagId, AsnLen len,UniversalString *result, AsnLen *bytesDecoded,ENV_TYPE env)


{
BDecAsnOctsContent (b, tagId, len, result, bytesDecoded, env);
if ((result->octetLen % 4) != 0)

{
Asn1Error ("BDecUniversalStringContent: ERROR - Invalid UniversalString Format");
longjmp (env, -40);
}

} /**//* end of BDecUniversalStringContent() */
/******************************************************************************************/
/******************************VisibleString**************************************************/
VisibleString,具體定義不明確,直接給出eSNACC對串內容的判斷函數吧:
static int chkVisibleString(VisibleString *checkBuf)


{
unsigned int i;
char temp;

if (checkBuf == NULL)
return -1;
for (i = 0; i < checkBuf->octetLen; i++)

{
temp = checkBuf->octs[i];

/**//* Check A-Z */
if((unsigned int)temp > 128)

{
return -1;
}
}

return 0;

} /**//* end of chkVisibleString() */
/******************************************************************************************/
/******************************UTF8String***************************************************/
壓軸好戲在后頭,所以最后讓我們來分析一個最有價值含量的!
UTF-8是UNICODE的一種變長字符編碼又稱萬國碼,由Ken Thompson于1992年創建。現在已經標準化為RFC 3629。UTF-8用1到6個字節編碼UNICODE字符。eSNACC用字節串來表示UTF8String,但是有一個判斷這個字節串是否有效UTF8String的函數,并且還定義了UTF8String和wchar類型相互轉換的函數,或許從這些函數中,我們能學習eSNACC是怎么處理UTF-8編碼的。
我們先看一些定義和utf8的判斷函數:
typedef struct


{
unsigned char mask;
unsigned char value;
unsigned long maxCharValue;
} MaskValue;


/**//* Global Values */
#define MAX_UTF8_OCTS_PER_CHAR 6


const MaskValue gUTF8Masks[MAX_UTF8_OCTS_PER_CHAR] =
{

{ 0x80, 0x00, 0x0000007F }, /**//* one-byte encoding 標記為0XXX XXXX*/

{ 0xE0, 0xC0, 0x000007FF }, /**//* two-byte encoding 標記為110X XXXX*/

{ 0xF0, 0xE0, 0x0000FFFF }, /**//* three-byte encoding 標記為1110 XXXX*/

{ 0xF8, 0xF0, 0x0001FFFF }, /**//* four-byte encoding 標記為1111 0XXX*/

{ 0xFC, 0xF8, 0x03FFFFFF }, /**//* five-byte encoding 標記為1111 10XX*/

{ 0xFE, 0xFC, 0x07FFFFFF } /**//* six-byte encoding 標記為1111 110X*/
};
static bool IsValidUTF8String(UTF8String* octs)


{
unsigned long i;
unsigned int j;

if (octs == NULL)
return false;

i = 0;
while (i < octs->octetLen)

{

/**//* Determine the number of UTF-8 octets that follow the first */
for (j = 0; (j < MAX_UTF8_OCTS_PER_CHAR) &&
((gUTF8Masks[j].mask & octs->octs[i]) != gUTF8Masks[j].value); j++)
;


/**//* Return false if the first octet was invalid or if the number of
subsequent octets exceeds the UTF8String length */
if ((j == MAX_UTF8_OCTS_PER_CHAR) || ((i + j) >= octs->octetLen))
return false;


/**//* Skip over first octet */
i++;


/**//* Check that each subsequent octet is properly formatted */
for (; j > 0; j--)

{
if ((octs->octs[i++] & 0xC0) != 0x80)
return false;
}
}

return true;
}
首先通過掩碼來判斷第一個字節,確定當前這個字符是用幾個字節來表示的。可能的是1-6,如果不是就會報錯。如果是一個,那么當前這個字節就是這個字符了,也就是只要字符小于0x7F,就只需要一個字符:這對應了MaskValue中的maxCharValue。其余的情況類似。而函數末尾那個for說明了:utf8的格式為:如果一個字符用了大于1個字節來表示,那么除了第一個用于表長度的字節以外,后面的表值的字節必須是10XX XXXX的樣式。
那么一個wchar字符是如何用utf8來表示的呢?我們看看wchar -> utf8的函數:
int CvtWchar2UTF8(wchar_t *inStr, char **utf8Str)


{
size_t wLen;
unsigned int i, j, x, y;
size_t wchar_size = sizeof(wchar_t);
wchar_t temp_wchar;


/**//* Check parameters */
if ((inStr == NULL) || (utf8Str == NULL))
return -1;

wLen = wcslen(inStr);


/**//* Allocate and clear memory for a worst case UTF-8 string */
*utf8Str = (char*)calloc(wLen * (wchar_size / 2 * 3) + 1, sizeof(char));
if (*utf8Str == NULL)
return -2;


/**//* Convert each wide character into a UTF-8 char sequence */
for (i = 0, x = 0; i < wLen; i++)

{
temp_wchar = inStr[i];


/**//* Return an error if the wide character is invalid */
if (temp_wchar < 0)

{
free(*utf8Str);
*utf8Str = NULL;
return -3;
}


/**//* Determine the number of characters required to encode this wide
character */
for (j = 0; (j < MAX_UTF8_OCTS_PER_CHAR) &&
(temp_wchar > gUTF8Masks[j].maxCharValue); j++)
;


/**//* Return an error if the wide character is invalid */
if (j == MAX_UTF8_OCTS_PER_CHAR)

{
free(*utf8Str);
*utf8Str = NULL;
return -3;
}


/**//* Skip over the first UTF-8 octet and encode the remaining octets
(if any) from right-to-left. Fill in the least significant six bits
of each octet with the low-order bits from the wide character value */
for (y = j; y > 0; y--)

{
(*utf8Str)[x + y] = (char)(0x80 | (temp_wchar & 0x3F));
temp_wchar >>= 6;
}


/**//* Encode the first UTF-8 octet */
(*utf8Str)[x] = gUTF8Masks[j].value;
(*utf8Str)[x++] |= ~gUTF8Masks[j].mask & temp_wchar;


/**//* Update the UTF-8 string index (skipping over the subsequent octets
already encoded */
x += j;
}

return 0;

} /**//* end of CvtWchar2UTF8() */
本函數第一次為存儲utf8的串分配足夠多的內存:“如果UNICODE字符由2個字節表示,則編碼成UTF-8很可能需要3個字節,而如果UNICODE字符由4個字節表示,則編碼成UTF-8可能需要6個字節。”但是此處總是用3或者6個字節來存。
然后遍歷每一個wchar字符,獲取存放他所需的字節數,然后反序來設定字節內容。最后設定長度標記字節。對單字節表示的情況,其實這就是原值。
對應的utf8 -> wchar 函數:
int CvtUTF8towchar(char *utf8Str, wchar_t **outStr)


{
unsigned int len, i, j, x;
size_t wchar_size = sizeof(wchar_t);

if ((utf8Str == NULL) || (outStr == NULL))
return -1;

len = strlen(utf8Str);

/**//* Allocate and clear the memory for a worst case result wchar_t string */
*outStr = (wchar_t*)calloc(len + 1, sizeof(wchar_t));
if (*outStr == NULL)
return -2;


/**//* Convert the UTF-8 string to a wchar_t string */
i = 0;
x = 0;
while (i < len)

{

/**//* Determine the number of UTF-8 octets that follow the first */
for (j = 0; (j < MAX_UTF8_OCTS_PER_CHAR) &&
((gUTF8Masks[j].mask & utf8Str[i]) != gUTF8Masks[j].value); j++)
;


/**//* Return an error if the first octet was invalid or if the number of
subsequent octets exceeds the UTF-8 string length */
if ((j == MAX_UTF8_OCTS_PER_CHAR) || ((i + j) >= len))

{
free(*outStr);
*outStr = NULL;
return -3;
}


/**//* Return an error if the size of the wchar_t doesn't support the
size of this UTF-8 character */
if ((j > 2) && (wchar_size < 4))

{
free(*outStr);
*outStr = NULL;
return -4;
}


/**//* Copy the bits from the first octet into the wide character */
(*outStr)[x] = (char)(~gUTF8Masks[j].mask & utf8Str[i++]);


/**//* Add in the bits from each subsequent octet */
for (; j > 0; j--)

{

/**//* Return an error if a subsequent octet isn't properly formatted */
if ((utf8Str[i] & 0xC0) != 0x80)

{
free(*outStr);
*outStr = NULL;
return -3;
}

(*outStr)[x] <<= 6;
(*outStr)[x] |= utf8Str[i++] & 0x3F;
}
x++;
}


/**//* Reallocate the wchar string memory to its correct size */
if (x < len)

{
*outStr = (wchar_t*)realloc(*outStr, (x + 1) * sizeof(wchar_t));
if (*outStr == NULL)
return -2;
}

return 0;
}
以上這兩個函數都是利用掩碼值做一些按位操作,我一直想著有沒有辦法說明其意義,不過很遺憾,我實在沒想出能用漢語說明白的辦法。看來還是大家從計算機世界的語言去弄明白吧~
/******************************************************************************************/
完。