0xF0, UTF и AMF

Что будет если строку, которая содержит в себе 0xF0, передать на сервер из клиента? Все строки которые прийдут на сервер, по спецификации AMF0 (AMF3) будут c UTF кодировкой.

Когда BlazeDS «парсит» сообщение, из DataInput читается строка следующим образом:
protected String readUTF(int utflen) throws IOException
    {
        checkUTFLength(utflen);
        char[] charr = getTempCharArray(utflen);
        byte[] bytearr = getTempByteArray(utflen);
        int c, char2, char3;
        int count = 0;
        int chCount = 0;
      
        // in instanceof DaraInputStream
        in.readFully(bytearr, 0, utflen);

        while (count < utflen)
        {
            c = (int)bytearr[count] & 0xff;
            switch (c >> 4)
            {
                case 0:
                case 1:
                case 2:
                case 3:
                case 4:
                case 5:
                case 6:
                case 7:
                    /* 0xxxxxxx*/
                    count++;
                    charr[chCount] = (char)c;
                    break;
                case 12:
                case 13:
                    /* 110x xxxx   10xx xxxx*/
                    count += 2;
                    if (count > utflen)
                        throw new UTFDataFormatException();
                    char2 = (int)bytearr[count - 1];
                    if ((char2 & 0xC0) != 0x80)
                        throw new UTFDataFormatException();
                    charr[chCount] = (char)(((c & 0x1F) << 6) | (char2 & 0x3F));
                    break;
                case 14:
                    /* 1110 xxxx  10xx xxxx  10xx xxxx */
                    count += 3;
                    if (count > utflen)
                        throw new UTFDataFormatException();
                    char2 = (int)bytearr[count - 2];
                    char3 = (int)bytearr[count - 1];
                    if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80))
                        throw new UTFDataFormatException();
                    charr[chCount] = (char)
                        (((c & 0x0F) << 12) |
                         ((char2 & 0x3F) << 6) |
                         ((char3 & 0x3F) << 0));
                    break;
                default:
                    /* 10xx xxxx,  1111 xxxx */
                    throw new UTFDataFormatException();
            }
            chCount++;
        }
        // The number of chars produced may be less than utflen
        return new String(charr, 0, chCount);
    }


Ок. Смотрим в спецификацию AMF протокола:

AMF 0 and AMF 3 use (non-modified) UTF-8 to encode strings. UTF-8 is the abbreviation for 8-bit Unicode Transformation Format. UTF-8 strings are typically preceded with a byte-length header followed by a sequence of variable length (1 to 4 octets) encoded Unicode code-points. AMF 3 uses a slightly modified byte-length header; a detailed description is provided below and referred to throughout the document.
(hex)
:
(binary)
0x00000000 — 0x0000007F
:
0xxxxxxx
0x00000080 — 0x000007FF
:
110xxxxx 10xxxxxx
0x00000800 — 0x0000FFFF
:
1110xxxx 10xxxxxx 10xxxxxx
0x00010000 — 0x0010FFFF
:
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
In ABNF syntax, [RFC3629] describes UTF-8 as follows:
UTF8-char
=
UTF8-1 | UTF8-2 | UTF8-3 | UTF8-4
UTF8-1
=
%x00-7F
UTF8-2
=
%xC2-DF UTF8-tail
UTF8-3
=
%xE0 %xA0-BF UTF8-tail | %xE1-EC 2(UTF8-tail) | %xED %x80-9F UTF8-tail | %xEE-EF 2(UTF8-tail)
UTF8-4
=
%xF0 %x90-BF 2(UTF8-tail) | %xF1-F3 3(UTF8-tail) | %xF4 %x80-8F 2(UTF8-tail)
UTF8-tail
=
%x80-BF


Все законно вроде как по отношению к методу. Может быть проблема в подсчете длины? Смотрим:

/**
     * AMF 3 represents smaller integers with fewer bytes using the most
     * significant bit of each byte. The worst case uses 32-bits
     * to represent a 29-bit number, which is what we would have
     * done with no compression.
     * <pre>
     * 0x00000000 - 0x0000007F : 0xxxxxxx
     * 0x00000080 - 0x00003FFF : 1xxxxxxx 0xxxxxxx
     * 0x00004000 - 0x001FFFFF : 1xxxxxxx 1xxxxxxx 0xxxxxxx
     * 0x00200000 - 0x3FFFFFFF : 1xxxxxxx 1xxxxxxx 1xxxxxxx xxxxxxxx
     * 0x40000000 - 0xFFFFFFFF : throw range exception
     * </pre>
     *
     * @return A int capable of holding an unsigned 29 bit integer.
     * @throws IOException
     * @exclude
     */
    protected int readUInt29() throws IOException
    {
        int value;

        // Each byte must be treated as unsigned
        int b = in.readByte() & 0xFF;

        if (b < 128)
            return b;

        value = (b & 0x7F) << 7;
        b = in.readByte() & 0xFF;

        if (b < 128)
            return (value | b);

        value = (value | (b & 0x7F)) << 7;
        b = in.readByte() & 0xFF;

        if (b < 128)
            return (value | b);

        value = (value | (b & 0x7F)) << 8;
        b = in.readByte() & 0xFF;

        return (value | b);
    }


Длина, согласно спецификации, считается правильно.

Но почему AMF формирует UTF строку и записывает туда 0xF0 (а не выбрасывает исключение), а потом говорит по спецификации что наличие этого символа есть UTFDataFormatException ?! Затык. По крайней мере я не вижу обьяснения. Баг?

Ок. Предположив что баг, в методе можно просто «выбросить» этот символ из строки и подвинутся при 0xF0 на четыре бита вправо. В этом случае не будет исключения. Но строка будет не идентична той, которую имеет клиент. Вариант не рабочий (возможно, этого никто не заметит. Но представим что строка критически важна и потерян символ? Программа не должна решать что необходимо а что нет для пользователя).

Варианты? Есть еще один!


private final static String EMPTY_STRING = "";

private static char[] tempCharArray = null;



protected String readUTF(int utflen) throws IOException
    {
if (utflen == 0) 
			return EMPTY_STRING; 

byte bytearr[] = getTempByteArray(utflen);

		reader.readFully(bytearr, 0, utflen);

		String result = new String(bytearr, 0, utflen, "utf-8");

		return result;
}
.....

final static byte[] getTempByteArray(int capacity)
	{
		byte[] result = tempByteArray;
		if ((result == null) || (result.length < capacity))
		{
			result = new byte[capacity * 2];
			tempByteArray = result;
		}
		return result;
	}


Что в этом случае? Мы получили UTF строку и не получили исключение. Вроде как то что надо. Или я что то пропускаю?
  • +1
  • 20 августа 2010, 05:14
  • cyril

Комментарии (0)

RSS свернуть / развернуть

Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.
Не забываем смотреть статистику:

Яндекс цитирования