Вопрос

У меня есть протокол, для которого требуется поле длины до 32 бит, и оно должно быть сгенерировано во время выполнения, чтобы описать, сколько байт находится в данном пакете.

Приведенный ниже код является своего рода некрасиво, но мне интересно, если это может быть преобразована в быть немного более эффективным или легко объяснимо.Проблема в том, что код генерирует только достаточное количество байтов для описания длины пакета, поэтому менее 255 байт = 1 байт длины, менее 65535 = 2 байта длины и т.д...

{
    extern char byte_stream[];
    int bytes = offset_in_packet;
    int n = length_of_packet;
   /* Under 4 billion, so this can be represented in 32 bits. */
    int t;
   /* 32-bit number used for temporary storage. */

    /* These are the bytes we will break up n into. */
    unsigned char first, second, third, fourth;

    t = n & 0xFF000000;
    /* We have used AND to "mask out" the first byte of the number. */
    /* The only bits which can be on in t are the first 8 bits. */
    first = t >> 24;
    if (t)  {
        printf("byte 1: 0x%02x\n",first );
        byte_stream[bytes] = first; bytes++;
        write_zeros = 1;
    }
    /* Now we shift t so that it is between 0 and 255. This is the first, highest byte of n. */
    t = n & 0x00FF0000;
    second = t >> 16;
    if (t || write_zeros) {
        printf("byte 2: 0x%02x\n", second );
        byte_stream[bytes] = second; bytes++;
        write_zeros = 1;
    }

    t = n & 0x0000FF00;
    third = t >> 8;
    if ( t || write_zeros) {
        printf("byte 3: 0x%02x\n", third );
        byte_stream[bytes] = third; bytes++;
        write_zeros = 1;
    }

    t = n & 0x000000FF;
    fourth = t;
    if (t || write_zeros) {
        printf("byte 4: 0x%02x\n", fourth);
        byte_stream[bytes] = fourth; bytes++;
    }
}
Это было полезно?

Решение

На самом деле вы выполняете только четыре вычисления, поэтому удобочитаемость здесь важнее , чем эффективность. Мой подход сделать что-то вроде этого более читабельным заключается в

<Ол>
  • Извлечение общего кода в функцию
  • Соедините похожие вычисления, чтобы сделать шаблоны более очевидными
  • Избавьтесь от промежуточной переменной print_zeroes и объясните, в каких случаях вы выводите байты, даже если они равны нулю (т. е. предыдущий байт был ненулевым)
  • Я изменил блок случайного кода на функцию и изменил несколько переменных (подчеркивание доставляет мне проблемы на экране предварительного просмотра уценки). Я также предположил, что bytes передается и тот, кто передает его, передаст нам указатель, чтобы мы могли его изменить.

    Вот код:

    /* append byte b to stream, increment index */
    /* really needs to check length of stream before appending */
    void output( int i, unsigned char b, char stream[], int *index )
    {
        printf("byte %d: 0x%02x\n", i, b);
        stream[(*index)++] = b;
    }
    
    
    void answer( char bytestream[], unsigned int *bytes, unsigned int n)
    {
        /* mask out four bytes from word n */
        first  = (n & 0xFF000000) >> 24;
        second = (n & 0x00FF0000) >> 16;
        third  = (n & 0x0000FF00) >>  8;
        fourth = (n & 0x000000FF) >>  0;
    
        /* conditionally output each byte starting with the */
        /* first non-zero byte */
        if (first) 
           output( 1, first, bytestream, bytes);
    
        if (first || second) 
           output( 2, second, bytestream, bytes);
    
        if (first || second || third) 
           output( 3, third, bytestream, bytes);
    
        if (first || second || third || fourth) 
           output( 4, fourth, bytestream, bytes);
     }
    

    Еще немного эффективнее, и может быть легче понять, будет ли это изменение последних четырех операторов if:

        if (n>0x00FFFFFF) 
           output( 1, first, bytestream, bytes);
    
        if (n>0x0000FFFF) 
           output( 2, second, bytestream, bytes);
    
        if (n>0x000000FF)  
           output( 3, third, bytestream, bytes);
    
        if (1) 
           output( 4, fourth, bytestream, bytes);
    

    Я согласен, однако, что сжатие этого поля усложняет получение конечного автомата. Но если вы не можете изменить протокол, этот код будет намного легче читать.

    Другие советы

    Вам действительно следует использовать поле фиксированной ширины для вашей длины.

    • Когда программа на принимающей стороне должна прочитать поле длины вашего пакета, как она узнает, где заканчивается длина?
    • Если длина пакета потенциально может достигать 4 ГБ, действительно ли имеют значение накладные расходы в 1-3 байта?
    • Вы видите, насколько сложным уже стал ваш код?

    Попробуйте этот цикл:

    {
        extern char byte_stream[];
        int bytes = offset_in_packet;
        int n = length_of_packet; /* Under 4 billion, so this can be represented in 32 bits. */
        int t; /* 32-bit number used for temporary storage. */
        int i;
    
        unsigned char curByte;
    
        for (i = 0; i < 4; i++) {
            t = n & (0xFF000000 >> (i * 16));
    
            curByte = t >> (24 - (i * 8));
            if (t || write_zeros)  {
                printf("byte %d: 0x%02x\n", i, curByte );
                byte_stream[bytes] = curByte;
                                bytes++;
                write_zeros = 1;
            }
    
        }
    
    }
    

    Я не уверен, что понимаю ваш вопрос. Что именно вы пытаетесь посчитать? Если я правильно понимаю, вы пытаетесь найти самый значимый ненулевой байт.
    Возможно, вам лучше использовать такой цикл:

    int i;  
    int write_zeros = 0;  
    for (i = 3; i >=0 ; --i) {  
        t = (n >> (8 * i)) & 0xff;  
        if (t || write_zeros) {  
            write_zeros = 1;  
            printf ("byte %d : 0x%02x\n", 4-i, t);  
            byte_stream[bytes++] = t;
        }  
    }
    
    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top