Гепардово гнездо
638 subscribers
41 photos
1 file
81 links
El nido del guepardo.

@gepardius тащит в гнездо всякое интересное из мира программирования (и не только).

Etaoin shrdlu cmfwyp!

Еще один мой канал: @sofcheck.
Download Telegram
Про throwing values

Расскажу коротко про throwing values (они же иногда называются herbceptions, потому что предложил их Herb Sutter). Полностью прочитать можно здесь, а я лишь отмечу основные моменты:
* функции, которые кидают исключение, помечены как throws, при этом по сути они возвращают исключение просто как значение, в регистрах (почти как Result<T, E> в Rust и std::expected<>)
* такие «исключения» пробрасываются автоматически и синтаксически ловятся try/catch как обычные, хотя на самом деле в этом случае раскрутка стека не используется. Конечно, можно их так не реализовывать, а сделать почти как в Rust и требовать подобие оператора ?, чтобы явно пробрасывать ошибку
* все исключения лежат в типе std::error размером с два указателя (чем-то похоже на гошный тип error и существующий в C++ std::error_code). Первая половина — это категория ошибки, а вторая — ее содержание (просто код ошибки для простых случаев, указатель на подробное описание для сложных, в зависимости от категории). В статье приводится также мотивация в пользу единого типа ошибок std::error вместо кучи разных типов как в текущей реализации исключений

Также в proposal'е предлагается добавить _Either(T, E) в Си для поддержания совместимости, а еще оптимизации вида «юзать регистр флагов, чтобы понять, вернулась ошибка или значение».

На картинке выше (которую я честно украл из статьи) показывается сравнение синтаксиса throwing values и std::expected<>, где видно, что код со throwing values выглядит гораздо проще.

Наверное, можно было бы еще поговорить на эту тему, но я вас уже и так закидал кучей разного текста на сегодня, поэтому остановимся здесь :)
🐳6👍1
Драма в двух актах

(оригинал здесь: https://github.com/nim-lang/Nim/issues/23796)

(это я так законтрибутил фикс своей баги в Nim, а потом оказалось, что мой фикс ломает существующий код в специфичном сценарии)
😢6😎5
Недавно наткнулся на интересный пост про онлайн-безопасность (а точнее, ее отсутствие) в Южной Корее:

https://palant.info/2023/01/02/south-koreas-online-security-dead-end/

Если кратко:
- по историческим причинам крупные банки вместо TLS используют свою криптографию и протоколы для организации шифрованного соединения
- чтобы эти протоколы использовать, необходимо ставить сторонние расширения или приложения
- сами эти приложения защищены плохо и используют устаревшие и уязвимые библиотеки
- по этой же причине долгое время обязательно требовался Internet Explorer, но ситуация с тех пор поменялась
- никто эту проблему так и не решил (по крайней мере, на момент начала 2023 года, когда этот пост вышел)

Если честно, я шокирован, что такая ситуация может быть в целой стране, причем довольно развитой и технологичной
👍6😁3🤯1
Сегодня нашел забавное

Есть вот такая репа со «Standard Go Project Layout» (48К звезд, между прочим). Авторы подчеркивают, что, хотя организация и называется golang-standards, она не имеет никакого отношения к команде разработки Go. Создатели репы утверждают, что посмотрели, какой project layout используют разные проекты на Go, и сформировали из этого свой «стандарт»:
This is NOT an official standard defined by the core Go dev team. This is a set of common historical and emerging project layout patterns in the Go ecosystem. Some of these patterns are more popular than others. It also has a number of small enhancements along with several supporting directories common to any large enough real world application.


Но однажды к ним пришел сам Russ Cox (один из core разработчиков Go) и заявил, что их «стандарт» вовсе не стандарт:

The README makes clear that this is not official, but even the claim "it is a set of common historical and emerging project layout patterns in the Go ecosystem" is not accurate.

For example, the vast majority of packages in the Go ecosystem do not put the importable packages in a pkg subdirectory. More generally what is described here is just very complex, and Go repos tend to be much simpler.

It is unfortunate that this is being put forth as "golang-standards" when it really is not. I'm commenting here because I am starting to see people say things like "you are not using the standard Go project layout" and linking to this repo.


Issue, кстати, в итоге просто закрыли, а обсуждение ограничили. Такие вот дела 🐳
😁11👍1🤯1😱1😢1🤡1🐳1
Есть такой сайт в интернетах: https://tylervigen.com/spurious-correlations. Это наглядная демонстрация того, что корреляция не обязательно означает причинно-следственную связь, и может быть лишь удачным совпадением.

Работает он очень просто:
* собираем много разных графиков на много разных тем (около 20'000 штук!)
* попарно пробуем вычислить корреляцию между ними
* собираем все случаи, когда величины коррелируют с хорошей такой достоверностью
* публикуем :)

Сайт существует довольно давно, какое-то время назад он был сломан. Сегодня я зашел и увидел, что он снова работает и получил обновление в духе нынешнего времени:
* добавились «объяснения» корреляций с помощью LLM'ок
* также LLM'ками генерятся «научные» статьи вокруг найденной корреляции

Получилась еще и довольно забавная иллюстрация того, как можно «объяснить» все, что угодно :)
🔥13👍7🤯1
Чем я заnimался

Все описанное в посте происходило несколько месяцев назад, и у меня долго не доходили лапки оформить пост, но все же…

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

Я уже писал ранее про свои коммиты в Nim, но с тех пор пришлось еще немного в него поконтрибутить. На этот раз не в стандартную библиотеку, а аж в сам компилятор!

В современных языках обычно принято писать компиляторы на них же самих. Nim здесь не исключение. Когда-то давным-давно его компилятор был написан на Object Pascal, но с тех пор весь код на паскале автоматически сконвертили в код на Nim. Результат этой конвертации хорошо описывается словами из compiler/readme.md:
So the code is not a poster child of good Nim code.


Другая особенность компилятора Nim — это то, что он не использует LLVM (хотя существует сторонняя реализация, которая юзает LLVM как бэкенд компиляции), а транслирует код в Си, и только потом уже собирает бинарник при помощи gcc, clang или любого другого компилятора, который существует в системе. Подход хорош тем, что позволяет очень и очень просто заюзать любую функцию из любой библиотеки на Си (просто указываешь, какую функцию из какого хедера ты хочешь импортировать в Nim, и какую библиотеку надо влинковать — и все работает. А этот ваш Rust так может?). Еще можно попросить компилятор скомпилить код не в Си, а C++ — это может пригодиться для интеропа с C++ и позволяет юзать классы из кода на C++ в коде на Nim.

Конечно же, у такой двухэтапной компиляции есть и проблемы. Основная заключается в том, что, вообще говоря, Си и C++ — языки с достаточно большим количеством странностей и не всегда консистентным поведением. Например, ссылки в C++, которые умеют неявно разыменовываться, а сами ссылки нельзя никак изменять. Или массивы фиксированного размера в Си, которые ведут себя иногда как значение, а иногда как указатель на первый элемент — зависит от того, где и как массив используется. И, конечно же, при переводе кода на C/C++ эти все особенности надо учитывать. Естественно, учет особенностей приводит к костылям и багам в компиляторе. А избегать их иногда сложно, потому что хочется хороший интероп с C/C++.

Я несколько раз находил баги компилятора (раз, два, три) разной степени критичности. Кстати, баги в Nim исправляют довольно оперативно — через неделю-две созданный мной issue уже оказывался исправленным и закрытым. Но вот один раз мой баг игнорили аж целую неделю, поэтому я решил попробовать свои силы и исправить его самостоятельно.

Десяток часов времени, горстка отладочных echo по коду — и баг исправлен. Пора делать PR. Кстати, довольно много времени ушло не на то, чтобы понять причину бага, а на то, чтобы понять, как наиболее правильно его исправить и не поломать старые костыли, учитывающие всевозможные хитрые случаи. А еще в случае с C++ для фикса надо было уметь копировать ссылку — это сложная задача, поэтому пришлось немного покостылить и сделать так, чтобы вместо нее копировался указатель.

В итоге баг повержен, PR залит, issue закрыт. Победа? А вот и нет!

Для начала пришлось немного попотеть, чтобы бэкпортировать фикс в stable ветку. При попытке это сделать падали тесты, и пришлось черри-пикнуть в stable еще один коммит, чтобы все заработало.

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

Выводы из всей этой истории делайте сами. Могу лишь сказать вот что: если вам нравится язык, смотреть исходники его компилятора может быть не лучшей идеей — можно разочароваться :) Тем не менее, даже несмотря на такой опыт с багами, я все еще считаю, что Nim как язык неплох.

Стоит отметить, что описанные выше проблемы с компиляцией пытаются потихоньку решить. В частности, авторы языка изобрели промежуточный байткод между самой логикой Nim и C/C++ бэкендами, что позволит, я надеюсь, упростить компилятор и уменьшить количество багов.
👍17🤯2🤔1
Чем я еще заnimался

Расскажу, пожалуй, еще одну интересную историю про Nim.

Но сначала познакомлю вас с действующими лицами сего повествования:

* nim: компилятор Nim, лучшего языка программирования в мире. Можете считать примерным аналогом rustc из мира Rust.
* nimble: пакетный менеждер для языка Nim. Примерный аналог cargo из мира Rust.
* choosenim: утилита, которая умеет устанавливать nim и nimble нужных версий, а также обновлять их. Примерный аналог rustup из мира Rust.

Теперь можно начинать рассказ.

Однажды я заметил, что мой CI-пайплайн программы на Nim замедлился аж на целых пять минут! Все мы прекрасно помним xz и Jia Tan, и как из-за тормозов openssh был найден очень хорошо спрятанный бэкдор. Но в случае с openssh замедление составляло сотни миллисекунд, а здесь — аж целых пять минут! Паранойя зашкаливает, желание разобраться — еще больше.

В итоге, используя метод grep'а и внимательного чтения нагрепанного кода (очень полезный метод, кстати), я выяснил вот что:

* choosenim скачивает бинарники nim и nimble (и еще кучу разных других нужных вещей), и распаковывает их
* перед тем, как создать ссылки на распакованные бинарники в нужной папке, choosenim зовет nimble --version, чтобы напечатать предупреждение, если версия nimble слишком древняя и не поддерживается
* nimble перед тем, как показать свою версию, проверяет свое окружение, и обнаруживает, что компилятора nim нет на месте
* далее nimble пытается исправить это безобразие. Для этого он сам клонирует репу с компилятором nim и собирает его из исходников!
* сборка компилятора — конечно же, дело небыстрое, и занимает те самые пять минут. Скажем лишь спасибо, что компилятор собирается довольно быстро, и не приходится ждать часами.

В общем, предсказуемо никакого Jia Tan'а здесь не оказалось, зато оказался банальный баг. А с багами поступают обычно просто: репортят и ждут фикса. Пофиксили его, впрочем, быстро и легко: теперь --version лишь выводит версию и больше ничего сделать не пытается.

Выводы из этой истории делайте сами.
👍11🤯1🕊1
😁28🤯3👍1
Я написал очень длинный и очень интересный текст про Юникод. Поскольку в Telegram пост такого размера не помещается, выложил на сайт:

https://blo.gepar.do/v0/unicode.html

Все бегом читать :)
👍27🔥8🤯4
Вдогонку про Юникод и Rust

Во-первых, спасибо всем, кто читал и распространял мой пост про Юникод :) Формат с большими текстами на сайте оказался, как видно, довольно неплох, и надо будет когда-нибудь попробовать его еще раз…

Во-вторых, хотелось бы рассказать еще немного про крейт unicode_categories в Rust, который упоминался в посте. Крейт этот примечателен вот чем:
* С одной стороны, он заброшен и не развивается (последний релиз в 2016 году, последний коммит в репу — в 2021), а еще имеет версию 0.1.1 и не поддерживает новые версии Юникода.
* С другой стороны, он очень активно используется: под 80'000 скачиваний ежедневно, занимает 623 место по общему числу скачиваний, и от него зависят многие другие крейты. Какое-то время назад он даже использовался в rustfmt, но, к счастью, его там заменили на более современный unicode-properties.

Выводы делайте сами. Мне вспоминается лишь https://xkcd.com/2347 :)
🐳3
This media is not supported in your browser
VIEW IN TELEGRAM
Про переписывание истории

В баше (а точнее, в GNU Readline, который используется башом), есть довольно странная фича: историю команд можно редактировать!

Делается это так:
* нажимаем клавишу ↑, доматываем до команды, которую хотим отредактировать
* редактируем команду
* нажимаем клавишу ↓, доматываем до конца
* готово :)

Наглядную демонстрацию можно видеть на видео выше.

Я на эту «фичу» неумышленно натыкался много раз, и, сам того не зная, редактировал историю. А потом недоумевал, почему у меня при вызове history часть строк оказывались пустыми.

Как это отключить? Можно почитать, например, здесь. Если коротко, то надо в домашней папке создать файл с названием .inputrc и поместить в него такие строчки:

$include /etc/inputrc
set revert-all-at-newline on
😁12🔥7👌2🤬1🤨1
Еще про проклятые фичи баша

https://yossarian.net/til/post/some-surprising-code-execution-sources-in-bash

tl;dr: вот эта функция на баше при передаче «правильного» аргумента может привести к выполнению произвольного кода:
function guess() {
num="${1}"
if [[ "${num}" -eq 42 ]]
then
echo "Correct"
else
echo "Wrong"
fi
}


Мораль проста: не пишите на баше не передавайте в bash-скрипты недоверенные данные
😱5👍2
https://www.ryanliptak.com/blog/every-rc-exe-bug-quirk-probably/

Не знаю, как вы, а я просто обожаю такие посты.

Здесь автор попытался написать альтернативную реализацию компилятора ресурсов Windows. Это программа, которая принимает текстовый .rc файл с описанием менюшек, кнопочек, окошек, иконок, курсоров и прочего, и компилирует это описание в бинарный .res файл, который потом встраивается в бинарник приложения под Windows.

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

Как достичь этой цели? Например, можно написать код, а затем пофаззить оригинальную реализацию с альтернативной и найти, на каких данных они выдают разные результаты. В результате этого процесса автор нашел горы странного поведения в компиляторе ресурсов от Microsoft.

Пост длинный; если хотите посмотреть только самое интересное, можно поискать по странице по словам utterly baffling. Если же у вас много времени и вам не лень, можно прочесть и все :)
👍10🔥3👌1
Сегодня читал про то, как Debian везде перешел на 64-х битный time_t. Изменение войдет в релиз Debian 13 (будет выпущен летом/осенью следующего года).

Проблема очень важная, ведь в 2038 году 32-х битный time_t переполнится, и многие компьютеры по всему миру не смогут узнать правильное время! А учитывая, что до 2038 года осталось немногим более 13 лет (осталось всего-то пережить пару эпидемий, восстание машин и ядерную войну), то проблему надо как-то решать!

Конечно же, не все настолько драматично. Большинство процессоров в мире уже давно как 64-х битные, и юзают «правильный», 64-х битный time_t по умолчанию почти везде. Но старые, 32-х битные системы все еще существуют, и для них надо что-то придумать.

Итак, для решения проблемы 2038 года в Debian приняли ряд компромиссных решений странных костылей. Следите внимательно, чтобы не запутаться :)
* Для 64-х битных систем time_t уже и так 64-х битный, поэтому весь «переход» заключается лишь в переименовании пакетов (например, libcurl4libcurl4t64). Новые пакеты с t64 при этом «предоставляют» пакет со старым именем, чтобы сторонние программы не заметили поломки ABI, не получили неудовлетворенных зависмостей и продолжали себе спокойно работать.
* Для i386 (так в Debian называют 32-х битные Intel'ы) пакеты тоже получили суффикс t64, но time_t там так и остался 32-х битным 🐳🐳 Как и на 64-х битных архитектурах, новые пакеты с t64 «предоставляют» старые. Так сделано, потому что либы под i386 очень уж часто юзаются для запуска старых программ, которые невозможно перекомпилить, поэтому поломка ABI для них нежелательна. А суффикс t64 все равно приписали, видимо, потому что переименование пакетов сделали для всех архитектур сразу.
* Для других 32-х битных архитектур (armel, armhf) пакеты тоже получили t64 суффикс, но при этом они получили еще и 64-х битный time_t.

Кстати, в Ubuntu этот переход тоже проделали, и пакеты с суффиксом t64 уже есть в последнем LTS-релизе, 24.04.

Выводы из всей этой истории делайте сами.
🤨75🐳1
https://habr.com/ru/post/472970/

Статья 2019 года, то есть довольно старая, с учетом того, сколько всего разного с тех пор добавили в Telegram (включая комментарии, истории и анимированных китов 🐳)

В ней описывается куча странных технических решений и множество костылей в Telegram и его протоколе MTProto.

Насколько все улучшилось в Telegram со времен написания этой статьи? Я не знаю, но мне кажется, с учетом всего добавленного за пять лет, костылей и подпорок в нем стало сильно больше…

К сожалению, слова «часть 1» в заголовке — неправда, и вторая часть так и не вышла :(
Please open Telegram to view this post
VIEW IN TELEGRAM
💊3👍2🤯1🤡1
Итак, расскажу о том, что я сделал еще давно, но про что все никак не доходили руки написать пост.

Несколько лет назад, когда я разрабатывал шахматного бота под названием SoFCheck, мне понадобилось как-то проверять, действительно ли я улучшаю силу его игры, а также сравнивать его с другими ботами. Для этого я написал на Free Pascal утилиту под названием battlefield, которая запускает матчи между двумя ботами, считает всякие статистики и говорит, кто круче и насколько. Работал battlefield в целом довольно неплохо.

Не так давно мне захотелось сделать battlefield интереснее, чтобы можно было не только смотреть результаты матча, а наблюдать за партиями в реальном времени. Так появился day20. Он, как и его предшественник, тоже позволяет запускать матчи и смотреть статистики. Но теперь можно еще наблюдать за партиями в реальном времени.

Построен day20 довольно просто. Основной код написан на Go, в качестве базы — SQLite, а фронт сделан с помощью Go'шного стандартного шаблонизатора, HTMX и горстки обычного JS. Такой сетап, конечно, не дает масштабироваться до небес и не дает много девяток, но зато просто деплоится и задачу в целом выполняет: для запуска нужно всего два бинарника и все :)

Почему два? Дело в том, что day20 состоит из двух частей: основного сервера и сервера комнат. Первый общается с базой и с пользователем и рисует красивый UI. А второй принимает команды от основного сервера и запускает сами матчи. Такое разделение позволяет поднять основной сервер на не очень мощной машинке в облаке и крутить его постоянно. А самих ботов, которые требуют сильно больше CPU, можно запустить хоть на ноутбуке (либо на временной виртуалке). Серверов комнат, кстати, может быть сколько угодно (пока хватит ресурсов основного сервера принимать сделанные ходы и посылать новые матчи), что позволяет запускать много партий параллельно.

А теперь самое интересное: в ближайшие два дня вы можете зайти на https://day20.gepar.do и посмотреть все это в действии :) Там сейчас крутится матч между ботом от Антона @Wind_Eagle под названием Quirky (во много раз сильнее моего SoFCheck'а, кстати), и примерно равного ему по силе бота под названием Avalanche. При разработке Quirky, кстати, day20 неоднократно использовался для оценки его силы.
👍26🤯6