Обновить
17
1
Денис Тулупов@viruseg

_

Отправить сообщение

Рекомендую обратить внимание на Zstandard. Порт для .NET ZstdSharp. С 24 года поддерживается всеми браузерами.

Если убрать лишнее и отформатировать нормально, то вполне читаемо получается.

Бояться отсутствия инициализации нулями там, где совершенно точно, как в этом методе, не будет чтения из неинициализированного массива, - это какой-то карго-культ боязни unsafe.

У меня нет avx512 под рукой, но на avx2 это 11% разницы. Бесплатное повышение производительности с нулевыми рисками на дороге не валяется. Хотя нет, в этом случае как раз валяется.

На всякий случай копирую сюда метод, предложенный в статье, в котором есть сырые указатели и точно такая же не инициализированная нулями память.

private static unsafe string StackallocBuffer(string s)
{
    var buffer = stackalloc char[s.Length];
    var index = 0;

    foreach (var c in s)
    {
        if (!char.IsWhiteSpace(c))
        {
            buffer[index++] = c;
        }
    }

    return new string(buffer, 0, index);
}

Именно его я предложил заменить. Т.е. я буквально сделал предложенный в статье код менее опасным, заменив указатели на Span. И при этом сохранил производительность метода на том же уровне.

Span вместе с SkipLocalsInit всё ещё безопаснее, чем сырой указатель. Инициализации нулями не будет в обоих случаях. Поэтому, возвращаясь к изначальному сообщению, что не так с предложенным мной вариантом?

вы же видите что у вас Error примерно равен разнице результатов?

Вы сначала докопались к тому, чего не было в моём сообщении, а теперь доказываете то, что и так очевидно. Ради чего вы это делаете?

Но не требует у метода unsafe, и в коде не будет указателей char* buffer. Правда, нужно разжёвывать смысл моего сообщения?

Так, а я разве сказал, что через Span будет лучше/хуже? Я сказал, что будет то же самое, но без unsafe.

Вместо unsafe в StackallocBuffer лучше использовать Span, но пропустить инициализацию нулями с помощью атрибута SkipLocalsInit. Производительность будет точно такая же.

[SkipLocalsInit]
static string SpanBuffer(string s)
{
    Span<char> buffer = stackalloc char[s.Length];
    var index = 0;

    foreach (var c in s)
    {
        if (!char.IsWhiteSpace(c))
        {
            buffer[index++] = c;
        }
    }

    return new string(buffer[..index]);
}

Другие я даже не искал. Потому что использую именно GxHash, т.к. он очень быстрый. Поэтому и написал для него обертку. Уверен, что если кто-то ставил цель получить хеш массива структур, то решил эту задачу точно так же. Span используется повсеместно.

Вот, можете ознакомится с исходниками: https://github.com/viruseg/GxHash.128bits.Overloads

Т.е. интерпретируют массив структур, как непрерывную область памяти, и считают хэш от получившегося байтового буфера?

Всё так.

Библиотека из статьи вообще не считает хеши. О чём речь вообще, для кого плохие новости?

В моём коде нет никакого около-UB. Паддинги определяются через рефлексию здесь и сейчас. Генерация метода через IL - вполне официальный способ, а не хак. Остаётся лишь вопрос к тому, кто будет применять инструмент. Но это риторический вопрос. Я совершенно точно не в ответе за тех, кто пытается сделать подобные вещи без понимания их сути.

Я привёл пример с записью в файл. Проверка хеша, если не совпали, то идёт запись. Какая тут уязвимость может появиться? В худшем случае при несовпадении байт файл будет перезаписан, когда реальные данные не изменились. В примере с игрой в худшем случае будет выполнена тяжёлая логика лишний раз, но без проверки по хешу пришлось бы каждый кадр тратить большие ресурсы на эту же проверку. Естественно, что надо понимать, для чего используется подобный инструмент и какие опасности он несёт. Но это касается любых вещей. Очевидно, что подобный хеш не должен уйти в долговременное хранение, он предназначен для здесь и сейчас, для текущей сессии.

Если структура лежит в массиве или где-то внутри класса, JIT ничего с ней не сделает. Хеш массива помогает мгновенно понять, были ли в нём изменения. И если не было, то пропустить огромное количество логики. Полезно, например, в играх, где нужно каждый кадр пересчитывать состояние мира. Или когда нужно часто писать в файл, и вместо насилия над диском проще сравнивать хеш и писать только когда реально изменились данные.

А можно написать либу один раз и не писать ручками. Я не очень понимаю, о чём спор. Если вам не нужно, это не означает, что никому не нужно. Мы же тут программированием занимаемся. Если что-то можно автоматизировать, оно должно быть автоматизировано.

C# ли бы тоже выкинуть, где сделали что-то подобное? Или собственный код, где такой подход даёт ускорение, т.к. создаёт очень много таких структур и часто. А считать хеш нужно очень редко.

[SkipLocalsInit]
ExampleStruct[] Method0()
{
    Span<ExampleStruct> arr = stackalloc ExampleStruct[10];

    for (var i = 0; i < arr.Length; i++)
    {
        ref var s = ref arr[i];
        s.A = (byte) i;
        s.B = i * 10;
    }

    return arr.ToArray();
}

struct ExampleStruct
{
    public byte A;
    public int B;
}

Из плюсовой dll пришел массив структур, и у них паддинги набиты мусором. А нужно посчитать хеш, например, такой либой. И тут два варианта: или копировать их в новый массив, при чём нужно будет написать логику этого копирования, используя схожие техники, описанные мной в статье. Или обнулить паддинги. Это один пример из тысяч, зачем это нужно делать.

Пин-код назван именно пин-кодом, а не паролем чтобы не создавать путаницу, когда каждое из полей ввода имеет в своём имени слово Пароль. И в него как раз лучше всего вводить какой-то небольшой пароль, который легко удержать в памяти. А пароль из эмодзи не подразумевает только слова, там можно рисовать рисунок, который нельзя перебрать по словарю. Пин-код не обязательный для ввода, но поможет исключить коллизии с другими пользователями если будет одинаковый рисунок.

Простой рисунок с однократным нажатием.
Простой рисунок с однократным нажатием.
Рисунок основаны на цветовых градациях, но важен не только цвет, но и порядок нажатия для достижения цвета.
Рисунок основаны на цветовых градациях, но важен не только цвет, но и порядок нажатия для достижения цвета.

Основанная идея блока с эмодзи в том, что можно запомнить рисунок из эмодзи, а не сами эмодзи. Если их менять местами, то придется запоминать сами эмодзи или буквы на них. А скрытие эмодзи сделает невозможным или очень затруднительным ввод сложно рисунка.

Если использовать по 4 символа в кейворде, пин-коде и визуальном пароле. И ограничить себя латинским алфавитом и цифрами.

Всего позиций для символов: 3 фактора × 4 символа = 12 позиций

На каждой позиции может быть любое из 36 значений

Общее количество комбинаций: 36 × 36 × 36 × ... (12 раз) = 36^12 = 4 738 381 338 321 616 896

Процесс генерации пароля в Visual Password многоступенчатый и довольно сильно избыточный, основанный на множественном перемешивании байт, добавлении к ним огромного количества соли, множественного вычисления SHA-256 хешей и шифрования AES-GCM. Т.е. генерация не мгновенная. Если взломщик не держит в своём распоряжении квантовый суперкомпьютер, то шансы подобрать пароль у него примерно никакие.

Расчёты выше не учитывают, что длину пароля взломщик тоже не знает, а это тоже огромный такой фактор. И количество символов в кейворде и пин-коде не ограниченно четырьмя и латинским алфавитом.

И никто не мешает вам генерировать пароль длиной 100 символов, но брать из него только 98, делая отступ по одному символу в начале и в конце. А ещё можно добавлять свой символ в определённую позицию пароля. И такие махинации сделают абсолютно бесполезным перебор всех комбинаций, но по-прежнему сохранят главную фичу Visual Password – возможность использовать очень сложный пароль, не записывая его.

1
23 ...

Информация

В рейтинге
1 707-й
Откуда
Россия
Дата рождения
Зарегистрирован
Активность