Найти в Дзене
ZDG

zlib.decompress() для игры RDS на Rust

Я немного забуксовал в написании игры RDS, так как заимел довольно дерзкие планы на будущее. Во-первых, я хочу графику наконец сделать с привлечением OpenGL, ведь надо когда-то начинать. Но для OpenGL нужны текстуры с альфа-каналом, а для текстур с альфа-каналом нужны изображения в формате PNG, а для изображений в формате PNG нужно подключать зависимость SDL2_image. Помню, в прошлый раз что-то там не завелось, а в этот раз и проверять не стал. Вместо этого я буду делать свою поддержку PNG, потому что кроме чтения этого формата мне больше ничего не требуется. Бегло ознакомившись с описанием формата PNG, я убедился, что там в принципе ничего сложного нет, за исключением блоков данных, сжатых в формате DEFLATE, который используется в утилите zlib. Так что для распаковки данных нужно было подключать к проекту какую-то из многочисленных реализаций zlib, что мне опять же не понравилось и я решил проверить, что там. А там обычное сжатие Хаффмана, про которое я писал тут: И для дополнительного

Я немного забуксовал в написании игры RDS, так как заимел довольно дерзкие планы на будущее.

Во-первых, я хочу графику наконец сделать с привлечением OpenGL, ведь надо когда-то начинать.

Но для OpenGL нужны текстуры с альфа-каналом, а для текстур с альфа-каналом нужны изображения в формате PNG, а для изображений в формате PNG нужно подключать зависимость SDL2_image.

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

Бегло ознакомившись с описанием формата PNG, я убедился, что там в принципе ничего сложного нет, за исключением блоков данных, сжатых в формате DEFLATE, который используется в утилите zlib.

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

А там обычное сжатие Хаффмана, про которое я писал тут:

И для дополнительного сжатия Хаффман комбинируется с алгоритмом LZ77, который концептуально похож на LZW:

Я всё ещё колебался, когда нашёл прекрасную статью с исходниками на Питоне:

Let's implement zlib.decompress()

Там всё толково и пошагово расписано, а финальная реализация занимает всего 220 строк.

Статья на английском, но в случае необходимости можно воспользоваться гугл-переводчиком для страниц сайтов.

Смысла всё перепечатывать сюда не вижу, и даже полностью статью я не осилил (в части LZ77). На это пока нет времени. Вместо этого я провёл эксперимент – если просто построчно перевести код с Питона на Rust, даже не понимая, что он делает, получится ли рабочая программа?

Да, она получилась.

Но какой ценой?

Из-за разницы моделей памяти и типов Питона и Rust не всё получилось перевести буквально.

Для структуры HuffmanTree (все названия – от автора оригинального кода) я сделал вектор-хранилище узлов, а навигация по дереву делается не через ссылки на узлы, а через индексы узлов в хранилище.

-2

Конечно, никаких двойных мутабельных заимствований, из-за чего я получаю копию узла, а модификацию в хранилище делаю отдельно:

-3

Остальное, что довольно удивительно, особых проблем в Rust не вызвало. Если на Питоне попадалась какая-то особо заковыристая конструкция, типа

bl_count = [sum(1 for x in bl if x == y and y != 0) for y in range(MAX_BITS+1)]

Я просто просил DeepSeek переделать её на Rust. Он переделывал и попутно объяснял, что там вообще происходит, я это естественно перепроверял.

Надо сказать, на Rust получалось длиннее, но понятней:

-4

Да впрочем, до понятности и тут далеко.

Ещё есть такой момент на Питоне:

for i in range(n-1, -1, -1):

Это итератор, который работает по диапазону от n-1 до 0 с шагом -1.

В Rust это делается иначе: берётся обычный диапазон 0..n и разворачивается:

for i in (0..n).rev() {

За выделениями памяти в куче я тут уже не следил, пусть выделяет что хочет, а Rust даст гарантию безопасности. Расчёт у меня на то, что чтение PNG понадобится только один раз, поэтому вся память, выделенная во время чтения, будет сразу же вся и освобождена, так что никакой фрагментации возникнуть не должно.

Исходный код на Rust:

deflate_decompress.rs

Ничего тут не описываю, потому что повторюсь, в статье оригинального автора всё подробно расписано и перевод через гугл также доступен. Можно сравнить реализации на Питоне и на Rust.

Мне теперь нужно сделать собственно чтение PNG, а потом перейти к манипулированию текстурами в OpenGL. Треугольник с раскрашенными вершинами я уже научился выводить.

Читайте дальше: