Утверждают, что указатель выровнен по некоторому значению

Есть ли гарантированный способ утверждать, что данный необработанный указатель выровнен по некоторому значению выравнивания?

Я посмотрел на функцию указателя aligned_offset, но в документах указано, что для нее допустимо давать ложные отрицательные значения (всегда возвращать usize::MAX), и что правильность не может зависеть от этого.

Я вообще не хочу возиться с выравниванием, я просто хочу написать утверждение, которое будет паниковать, если указатель не выровнен. Моя мотивация заключается в том, что при использовании некоторых низкоуровневых встроенных функций ЦП передача указателя, не выровненного с какой-либо границей, вызывает ошибку ЦП, и я бы предпочел получить паническое сообщение Rust, указывающее, где находится вызывающая его ошибка, чем SEGFAULT.

Пример утверждения (неверный согласно документам aligned_offset):

#[repr(align(64))]
struct A64(u8);

#[repr(align(32))]
struct A32(u8);

#[repr(align(8))]
struct A8(u8);

fn main() {
    let a64 = [A64(0)];
    let a32 = [A32(0)];
    let a8 = [A8(0), A8(0)];
    
    println!("Assert for 64 should pass...");
    assert_alignment(&a64);
    println!("Assert for 32 should pass...");
    assert_alignment(&a32);
    
    println!("Assert for 8, one of the following should fail:");
    println!("- full array");
    assert_alignment(&a8);
    println!("- offset by 8");
    assert_alignment(&a8[1..]);
}

fn assert_alignment<T>(a: &[T]) {
    let ptr = a.as_ptr();
    
    assert_eq!(ptr.align_offset(32), 0);
}

Детская площадка ржавчины.

Я не эксперт по выравниванию, но не могли бы вы просто привести указатель к usize и взять модуль желаемого выравнивания байтов? Что-то вроде (ptr as usize) % alignment == 0?

JMAA 22.04.2022 21:49

@JMAA Я сомневаюсь, что это будет так просто, поскольку базовое значение указателя зависит от архитектуры. 1. Этот ответ: stackoverflow.com/a/57970536/4646738 указывает на архитектуру, которая так не работает, 2. Если бы это было так просто, я бы ожидал, что стандартная функция просто сделает это, предостережение «может быть неверным» не было бы нужный.

V0ldek 22.04.2022 23:13

Справедливое замечание, но нужна ли вам поддержка архитектур, которые не представляют указатели таким образом, чтобы они работали так? Вопрос, который вы связали, предполагает, что единственный способ сделать версию, которая работала бы для каждой мыслимой архитектуры, - это написать код для конкретной архитектуры для каждого особого случая. Или вы просто соглашаетесь с тем, что по крайней мере x86, amd64 и aarch64 (AFAIK) работают так, и этого достаточно.

JMAA 23.04.2022 12:00

Я также ожидаю, что стандартная версия функции будет работать должным образом на всех таких архитектурах, поэтому вы можете просто добавить дополнительную проверку, чтобы увидеть, возвращает ли она usize::MAX и терпеть неудачу, если это так, если вы параноик. Но на самом деле, если вы не ориентируетесь на 8086 и одновременно не смотрите на> 16-битное выравнивание, это, вероятно, не проблема.

JMAA 23.04.2022 12:05

@JMAA Я стараюсь быть архитектурно-независимым, насколько это возможно, для моего текущего использования. Хотя, даже если я решу пойти с половинчатым решением, вопрос, можно ли это сделать в общих чертах, все еще не дает мне покоя. Вы могу требуете определенного выравнивания от распределителя памяти, поэтому интуитивно (для меня) должен быть какой-то способ попросить распределитель сообщить вам выравнивание.

V0ldek 23.04.2022 13:02

Я понимаю, но в определенный момент вы должны сократить свои потери. Если вы не ориентируетесь на процессоры до 32-битной версии или особенно странные микроконтроллеры или DSP, то буквально нет причин для дальнейшего беспокойства. Я очень сомневаюсь, что есть даже целевые опоры для ржавчины, где это не сработает.

JMAA 23.04.2022 19:46
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
6
86
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Просто чтобы удовлетворить свои собственные неврозы, я пошел и проверил источникptr::align_offset.

Существует много тщательной работы с крайними случаями (например, const-вычисление всегда возвращает usize::MAX, аналогично для указателя на тип нулевого размера, и паникует, если alignment не является степенью числа 2). Но суть реализации для ваших целей — это здесь: требуется (ptr as usize) % alignment == 0, чтобы проверить, выровнено ли оно.

Edit: This PR is adding a ptr::is_aligned_to function, which is much more readable and also safer and better reviewed than simply (ptr as usize) % alginment == 0 (though the core of it is still that logic).

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

Поэтому:

assert_eq!(ptr.align_offset(alignment), 0);

должно быть много для вашего утверждения.

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

И это объяснение, я думаю. Я предполагаю, что в такой эзотерической архитектуре не обязательно будет разумная реализация, она просто по умолчанию будет всегда возвращать usize::MAX и по-прежнему будет допустимой реализацией Rust для этой архитектуры.

V0ldek 23.04.2022 21:45

Да, я полагаю, нет никакой гарантии, что is_aligned вообще возможно на каждой архитектуре. Лучше просто позволить разработчикам std lib иметь дело с любым кодом, специфичным для архитектуры, который может потребоваться.

JMAA 23.04.2022 21:50

Ожидается PR для is_aligned() и is_aligned_to(): github.com/rust-lang/rust/pull/95643.

Chayim Friedman 26.04.2022 01:33

Сам по себе тот факт, что std не реализует что-то правильно для какой-то платформы, не означает, что Rust полностью не поддерживает эту платформу, а только то, что на уровне std он не поддерживается (по крайней мере, полностью). В этом случае также есть отличие от C: стандарт C99 гласит, что (§6.3.2.3 Указатели) «любой тип указателя может быть преобразован в целочисленный тип. За исключением ранее указанного, результат определяется реализацией. Если результат не может быть представлен в целочисленный тип, поведение не определено. Результат не обязательно должен находиться в диапазоне значений любого целочисленного типа."...

Chayim Friedman 26.04.2022 01:40

... в то время как ссылка на Rust гласит, что (doc.rust-lang.org/stable/reference/expressions/…) «Приведение из необработанного указателя к целому числу производит машинный адрес памяти, на которую ссылаются. Если целочисленный тип меньше, чем тип указателя, адрес может быть усечен; использование usize позволяет избежать этого». Таким образом, стандартная библиотека верна, потому что для этого требуется адрес машины - я прочитал это как фактический адрес после сегментации.

Chayim Friedman 26.04.2022 01:41

Это определение также совместимо с 8086 (хотя приведения потребуют больше операций для настройки адреса). Хотя это может быть не то, что вы ожидаете: для соответствия каждому адресу потребуется usize 32 бита. Это также необходимо для doc.rust-lang.org/stable/reference/…: «usize и isize имеют размер, достаточный для размещения всех адресов на целевой платформе». Но хорошо известно, что Rust плохо поддерживает сегментированные архитектуры, и некоторые недавние разработки (строгое происхождение) могут исправить эту ситуацию. Так что эти определения могут меняться.

Chayim Friedman 26.04.2022 01:45

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