Pull to refresh
21
0
Thomas Aprelev @aprlv

C Software engineer

Send message

control (^) со стрелками не пробовали? Вроде бы между столами переключает.

Если мне не изменяет память, из определенных автором типов, в ядре есть только u8, u16, u32; но на момент начала разработки ядра упомянутых мной стандартных типов еще не было, их стандартизовали только в C99.

Подскажите, возможно я что-то не понял, но зачем вместо стандартных типов из stdint.h и stdbool.h вы определяете типы с собственными именами? Нельзя ли ваши типы называть точно так же?

В приведенных вами "простых и понятных" типах char (byte) — знаковый или беззнаковый? А какую разрядность имеет long? Мне всегда казалось, что различные системы типов решают различные задачи.


Например, при реализации низкоуровневых примитивов очень важно, чтобы некоторые переменные были именно беззнаковыми 32-разрядными (uint32_t), другие, например, беззнаковыми 64-разрядными (uint64_t), а третьи — тоже беззнаковыми, но неопределенной разрядности (size_t). Код, который эти переменные использует, может, например, рассчитывать на конкретное поведение в случае переполнения (wrapping underflow/overflow).


А при реализации высокоуровневых примитивов разрядность переменных может уже не иметь значения, и там вполне достаточно char и int, а в некоторых случаях можно вообще обойтись без примитивных типов.


Поэтому, опять же, на мой взгляд, в удобном и при этом универсальном языке программирования окажутся и те, и другие типы. При этом я совсем не против забирать определения более специфичных типов из отдельных заголовочных файлов (модулей, пакетов, etc.), но совсем выкидывать их из языка мне кажется неправильным.

Спасибо. Про __cleanup__ знал, про __auto_type — нет

Например на случай санкционированных и не очень митингов

Об этом уже позаботились. ФЗ-54, статья 6:


Участники публичных мероприятий не вправе… скрывать свое лицо, в том числе использовать маски, средства маскировки, иные предметы, специально предназначенные для затруднения установления личности

Alexey_Alive, направьте меня в сторону автовывода типов? Не нашел среди C Extensions в документации GCC. RAII — это вы об атрибуте __cleanup__?

Пардон, мы же не о шарообразном коне в вакууме. Россия в 1998 году вроде как ратифицировала Конвенцию о защите прав человека, а там шестая статья закрепляет за каждым право на справедливое судебное разбирательство, а первая статья обязывает Россию его обеспечивать.

Если мы являемся теми, кого мы едим, наша еда — это те, кто ест нашу пищу. Это означает, что мы те, кто питается нами.

У меня парсер сломался

То, что вы предлагаете называть undefined, в стандарте называется unspecified. Код, содержащий undefined behaviour, не может быть корректным в частном случае, в отличие от кода, содержащего unspecified behaviour. Вы согласны с тем, что следующий код не содержит UB?


void foo(uintptr_t offset) {
  char *ptr = (char *) offset;
  char val = *ptr;
}

foo(0);

От компилятора зависит, как именно offset будет приведен к ptr.
От архитектуры и операционной системы зависит, что именно случится при чтении по адресу ptr.
На мой взгляд, здесь нет неопределенного поведения с точки зрения языковой грамматики.

AntonSazonov


Почему стандарт языка, а за ним все следом говорят о том, что разыменование указателя с адресом 0 является UB?

Прежде всего, неопределенным поведением является разыменование именно некорректного указателя, а не указателя с адресом 0:


C99, footnote 87
Among the invalid values for dereferencing a pointer by the unary * operator are a null pointer, an address inappropriately aligned for the type of object pointed to, and the address of an object after the end of its lifetime.

При этом под нулевым указателем понимается не указатель с адресом 0, а некоторая константа NULL, или nullptr с внутренним представлением, которое вполне может оказаться не 0. Другими словами, для корректного указателя ptr верно ptr != NULL, даже если (uintptr_t) ptr == 0.


C99, §6.3.2.3
An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant. If a null pointer constant is converted to a pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function.

P.S. Занимательно, что в таком случае указатели ptr1 и ptr2


uintptr_t zero = 0;
void
  *ptr1 = (void *) 0,  /* == NULL */
  *ptr2 = (void *) zero;  /* != NULL */

могут оказаться не равны, потому что zero для ptr2 не является integer constant expression.

Дак вот, я считаю, что стандарт об это ничего не говорит вообще.

Разве в C99 нет специального параграфа именно по поводу суперпозиции & и *?


C99, §6.5.3.2
The unary & operator yields the address of its operand.

If the operand is the result of a unary * operator,
neither that operator nor the & operator is evaluated and the result is as if both were
omitted, except that the constraints on the operators still apply and the result is not an
lvalue.

Даже больше, есть специальная сноска, явно определяющая &*NULL как корректную операцию:


C99, footnote 87
Thus, &*E is equivalent to E (even if E is a null pointer), and &(E1[E2]) to ((E1)+(E2)).

Оптимизация остатка от деления с дополнительной разностью — IMHO, весьма спорная оптимизация, она имеет смысл только для больших значений верхней границы. Если же верхняя граница невелика, то вам придется в среднем вычислять как остаток от деления, так и разность.

Разве t и (t - range) не равны по модулю range?
Оператор %= вычисляет остаток от деления (а не частное).

Для сборки в несколько потоков еще можно использовать cmake --build --parallel.


Мало где упоминается одна из важнейших возможностей CMake — INTERFACE targets, которые могут служить своеобразным контейнером для свойств сборки (путей поиска заголовочных файлов, флагов компиляции, и пр.):


add_library(super INTERFACE)
target_link_directories(super INTERFACE <dirs>)
target_compile_options(super INTERFACE <opts>)

# $A inherits properties from $super
add_library(A <srcs>)
target_link_libraries(A PRIVATE super)

Что касается перечисления файлов с исходным кодом с помощью file(GLOB), я сталкивался с двумя проблемами:


  1. Очевидна необходимость следить за актуальностью проекта вручную (эта проблема вроде решается с file(GLOB <pattern> CONFIGURE_DEPENDS) с CMake 3.12).
  2. file(GLOB) не сортирует перечисляемые файлы, что может привести к невоспроизводимым сборкам (эта проблема вроде решена встроенной сортировкой файлов по именам с CMake 3.6), если не сортировать полученный список файлов вручную c list(SORT).

Согласен, если в качестве единственного компилятора используется GCC (или Clang), то собственная реализация копирования памяти в большинстве случаев не потребуется. Но мне было необходимо поддерживать и другие компиляторы, у которых нет inline версии memcpy(), и для этих компиляторов использование своего копирования позволило увеличить производительность операции почти втрое. А чтобы код был один и тот же везде, для компиляторов, поддерживающих анализ совместимости типов, пришлось добавить атрибуты may_alias.

Почему-то не упомянули атрибут may_alias (GCC, Clang), который отключает анализ псевдонимов на совместимось типов, как один из самых простых способов произвести необходимые приведения типов без отключения -fstrict-aliasing:


/* This type lifts strict aliasing restrictions */
typedef
    uint64_t __attribute__((__may_alias__))
    uint64_aliased_t;

Это пригодилось для реализации быстрого копирования небольших выровненных массивов:


/* Aligned copy example;
 * dst and src pointers may have any effective type
 * (alignment issues aside)
 */
uint64_aliased_t *dst_ = dst;
uint64_aliased_t const *src_ = src;

for (size_t i = 0; i < size_; ++i)
    dst_[i] = src_[i];

Без этих атрибутов копирование, естественно, не работало для всех указателей dst и src, у которых эффективный тип был отличен от char. В частности, компилятор не выполнял копирование в том случае, когда один из указателей был получен в результате сведения (array type decay) массива alignas(8) uint32_t.

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


Я привел ссылку на свою статью о Кузнечике в надежде переубедить вас в вопросе эффективности предвычисленых таблиц ;)

Serge3leo Использование предвычисленных таблиц для Кузнечика позволяет ускорить его производительность на порядки, описание и результаты я уже публиковал, потому что позволяет не вычислять S-слой и упростить вычисление L-слоя. Таблица для преобразования в каждую сторону при этом занимает 64 кБ.


При этом использование SSE2 в дополнение к таблицам повышает его производительность чуть больше, чем в полтора раза. Эффективные реализации AES (в отсутствие AES-NI) тоже используют таблицы.

Прежде всего, какой мастер-ключ, о чем вы? Это же блочный шифр. Выбор "плохих" узлов замены может ослабить всю суперпозицию преобразований и, в простейшем, например, случае, облегчить нахождение секретного ключа по паре текстов. Знание о том, чем именно плохи эти узлы, и является той самой лазейкой, или, как вы ее называете, "закрытым мастер-ключом".

Information

Rating
Does not participate
Location
Москва, Москва и Московская обл., Россия
Registered
Activity