Проверьте и выровняйте буфер - PullRequest
0 голосов
/ 29 января 2020

Я пытаюсь понять, как проверить, выровнен ли указатель или нет, и в конечном итоге выровнять его.

Чтобы понять это, я беру эту функцию:

#define PJ_POOL_ALIGNMENT 8

PJ_DEF(pj_pool_t*) pj_pool_create_on_buf(const char *name,
                 void *buf,
                 pj_size_t size)
{
#if PJ_HAS_POOL_ALT_API == 0
struct creation_param param;
pj_size_t align_diff;

PJ_ASSERT_RETURN(buf && size, NULL);

if (!is_initialized) {
if (pool_buf_initialize() != PJ_SUCCESS)
    return NULL;
is_initialized = 1;
}

/* Check and align buffer */
align_diff = (pj_size_t)buf;
if (align_diff & (PJ_POOL_ALIGNMENT-1)) {
    align_diff &= (PJ_POOL_ALIGNMENT-1);
    buf = (void*) (((char*)buf) + align_diff);
    size -= align_diff;
}

param.stack_buf = buf;
param.size = size;
pj_thread_local_set(tls, &param);

return pj_pool_create_int(&stack_based_factory, name, size, 0, 
              pj_pool_factory_default_policy.callback);
#else
PJ_UNUSED_ARG(buf);
return pj_pool_create(NULL, name, size, size, NULL);
#endif
}

, очевидно, часть меня интересует следующее: / * проверка и выравнивание буфера * / единственное, что я понимаю, я понимаю, это:

давайте сосредоточимся на if. Это хочет проверить, выровнен ли буфер к 8-байтовому множественному адресу. Если условие if не выровнено, возвращается число, отличное от 0, и затем выполняется выравнивание, в противном случае достаточно того, что есть только бит с 1, чтобы пропустить IF. Чтобы получить этот результат, они делают переменную PJ_POOL_ALIGNMENT равной 7 (0111), и с этим они делают AND с адресом, где был выделен буфер. Операция выполняется следующим образом, учитывая, что я хочу получить число, отличное от 0, если адрес не выровнен.

0000 ... 0111 И

хххх. , , х100


0000 ... 0100 не выровнено

, если есть 1 (или больше 1) в любом из последних 3 битов, и поэтому я знаю, что он не выровнен с 8-байтовым блоком: x AND 1 = 0 , если будет верно. тогда он войдет в блок коррекции.

Но блок if мне неясен. Кто-то, кто может подтвердить правильность моих рассуждений и заставить меня понять блок.

1 Ответ

0 голосов
/ 29 января 2020

Текущий код выравнивания неверен. Он определяет разницу выравнивания от нижней границы выравнивания и неправильно добавляет ее к значению указателя для достижения верхней границы выравнивания:

xxxxx000 + 000 = xxxxx000 (OK - no change)
xxxxx001 + 001 = xxxxx010 (WRONG)
xxxxx010 + 010 = xxxxx100 (WRONG)
xxxxx011 + 011 = xxxxx110 (WRONG)
xxxxx100 + 100 = xxxxy000 (OK - rounded up)
xxxxx101 + 101 = xxxxy010 (WRONG)
xxxxx110 + 110 = xxxxy100 (WRONG)
xxxxx111 + 111 = xxxxy110 (WRONG)

Разница с верхней границей является дополнением 2 разницы с нижней границей выравнивания, по модулю размера выравнивания:

xxxxx000 + 000 = xxxxx000 (OK - no change)
xxxxx001 + 111 = xxxxy000 (OK - rounded up)
xxxxx010 + 110 = xxxxy000 (OK - rounded up)
xxxxx011 + 101 = xxxxy000 (OK - rounded up)
xxxxx100 + 100 = xxxxy000 (OK - rounded up)
xxxxx101 + 011 = xxxxy000 (OK - rounded up)
xxxxx110 + 010 = xxxxy000 (OK - rounded up)
xxxxx111 + 001 = xxxxy000 (OK - rounded up)

Текущий код выравнивания можно исправить, добавив одну строку, чтобы преобразовать нижнюю разницу выравнивания в верхнюю разницу выравнивания :

/* Check and align buffer */
align_diff = (pj_size_t)buf;
if (align_diff & (PJ_POOL_ALIGNMENT-1)) {
    align_diff &= (PJ_POOL_ALIGNMENT-1);
    align_diff = PJ_POOL_ALIGNMENT - align_diff; // upper alignment
    buf = (void*) (((char*)buf) + align_diff);
    size -= align_diff;
}

В качестве альтернативы, верхняя разница выравнивания может быть определена непосредственно перед if:

/* Check and align buffer */
align_diff = (pj_size_t)-(pj_size_t)buf & (PJ_POOL_ALIGNMENT-1);
if (align_diff != 0) {
    buf = (void*) (((char*)buf) + align_diff);
    size -= align_diff;
}

Можно утверждать (и было!), Что это менее читабельно чем оригинальная версия.

На самом деле, if можно было бы опустить, поскольку добавление нуля не имеет значения:

/* Check and align buffer */
align_diff = (pj_size_t)-(pj_size_t)buf & (PJ_POOL_ALIGNMENT-1);
buf = (void*) (((char*)buf) + align_diff);
size -= align_diff;

Относительно align_diff = (pj_size_t)-(pj_size_t)buf & (PJ_POOL_ALIGNMENT-1);, (pj_size_t)buf преобразует указатель в целочисленный тип без знака - отрицает значение, а начальный (pj_size_t) преобразует отрицательный va lue к целому типу без знака, используя арифметику дополнения до 2 c. & (PJ_POOL_ALIGNMENT-1) преобразует по модулю PJ_POOL_ALIGNMENT эквивалентно % PJ_POOL_ALIGNMENT (поскольку PJ_POOL_ALIGNMENT является степенью 2).

Строго, чтобы избежать неопределенного поведения , указатель выше целое преобразование должно выполняться с использованием uintptr_t (определяется #include <stdint.h>) вместо pj_size_t:

align_diff = (uintptr_t)-(uintptr_t)buf & (PJ_POOL_ALIGNMENT-1);

Что касается buf = (void*) (((char*)buf) + align_diff);, арифметика указателя c недопустима для значений void * (при минимум в стандартном C), поэтому (char*)buf преобразует его в указатель на char. Поскольку sizeof(char) равен 1 байту по определению, + align_diff продвигает указатель на align_diff байтов по мере необходимости. (void*) затем преобразует это обратно в void *, прежде чем присвоить его обратно buf. Этот (void*) может быть опущен в C (но не в C ++), поэтому утверждение можно переписать в виде:

buf = (char*)buf + align_diff;

, что, возможно, более читабельно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...