Files
ARSV/Docs/set:version.md
T
2026-07-03 00:04:36 +03:00

9.9 KiB
Raw Blame History

set:version

what is happening inside set:version

Актуальный код с наработками расположен в репозитории

Зачем

set:version в alt-rpm позволяет сопоставлять Provides и Requires пакетов не обычным сравнением версий, а сравнением специальных set-строк зависимостей вида

libfoo.so.X = set:<encoded-set>

encoded-set формируется на основе символов, необходимых/предоставляемых пакетом. Данный механизм позволяет гарантировать (с точностью до коллизий хэша, об этом будет далее) наличие всех требуемых символов в библиотеке. Это исключает ситуации, при которых ">= версий" ломается при удалении символа из библиотеки, а также ситуаций совпадения SONAME библиотек с разным набором символов.

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

Реализация set.c

set.c предоставляет 5 "публичных" API для работы set:version

int rpmsetcmp(const char *set1, const char *set2);

struct set *set_new(void);
void set_add(struct set *set, const char *sym);
const char *set_fini(struct set *set, int bpp);
struct set *set_free(struct set *set);

rpmsetcmp()

Основная функция, сравнивающая строки и выдающая результат в зависимости от включения:

  • 1: set1 > set2
  • 0: set1 == set2
  • -1: set1 < set2 (aka set1 \subset set2)
  • -2: set1 != set2
  • -3: set1 decoder error
  • -4: set2 decoder error

на основе данной заметки, set1 лучше делать как Provides для лучшей производительности.

set_new()

Создаёт пустой объект struct set.

Внутри struct set — это временный контейнер для строк символов и их будущих hash-значений. Из релизации:

internally struct set is just a bag of strings and their hash values.

set_add()

Добавляет строковый символ в set.

Использование:

set_add(s, "printf@@GLIBC_2.2.5");
set_add(s, "malloc@@GLIBC_2.2.5");

set_fini()

Финализирует множество и возвращает готовую set-version строку.

В качестве параметров принимает struct set *set и int bpp При bpp < 10 или bpp > 32 функция возвращает NULL.

Работа функции описана далее.

set_free()

Освобождает struct set и его внутренние строки.

!!! NOTE В данный момент не освобождается сама структура struct set, память под которую в set_new() выделяется с xmalloc

Работа внутри set_fini()

Основной процесс преобразования массива строк в set-строку происходит следующим образом:

массив строк
  | (Jenkins OATT)
  v
массив хэшей
  | (qsort)
  v
отсортированный массив хэшей
  | (вычисление разницы между элементами)
  v
массив delta
  | (Rice-Golomb преобразование)
  v
битовый массив
  | (base62 преобразование)
  v
set-строка

hash

Для каждого элемента (строки) высчитывается 32-битный хэш и обрезается до bpp бит

set->sv[i].v = hash(set->sv[i].s) & mask;

В качестве хэш-функции используется Jenkins OAAT.

Получившийся массив сортируется по хэш значениям по возрастанию. При коллизии, печатается warning: hash collision в stderr

С помощью функции uniqv() в массиве остаются только уникальные значения, возвращаемое значение - размер массива после удаления повторов.

Все дальнейшие преобразования происходят внутри функции encode_set(). Возвращаемое значение - длина итоговой set-строки, включая bpp и Mshift первыми двумя символами.

!!! NOTE Возвращаемое значение encode_set() может использоваться как код ошибки при отрицательных значениях

delta

Внутри функции encode_delta() происходит преобразование массива отсортированных по возрастанию чисел в массив дельт между числами, т.е. Пример исходного массива:

unsigned *v = {1, 4, 16, 22};

Пример результирующего массива:

unsigned *v = {1, 3, 12, 8};

Rice-Golomb

encode_golomb() - Main golomb encoding routine: package integers into bits.

Для сокращения длины итоговой строки применяется Rice-Golomb кодирование, которое позволяет с параметром Mshift записать число n как n >> Mshift, записанный последовательностью нулей и n & 2^Mshift записанный стандартным двоичным кодированием. Целая часть и остаток разделяется единичном битом.

Mshift выбирается как bpp - log2(c) - 1, т.к. при равномерном распределении c хэшей по диапазону 2^bpp, средняя delta будет равна 2^bpp / c

Пример:

Mshift = 8
n = 553
q = 553 >> 8 = 2
r = 553 & 255 = 42

bits = 00 1 00101010

Функция encode_golomb() возвращает длину итоговой битовой последовательности. Сама последовательность находится в char *bitv (char = [1,0])

base62

encode_base62() - pack bitv into base62 string

Последним шагом является преобразование битовой последовательности в base62 строку

Алфавитом для кодирования является 0-9,a-z,A-Z, но символ Z кодирует сразу 61, 62 и 63 следующим образом:

  1. в битовую последовательность после кодированием Z добавляется 2 бита:
    • 00 для 61
    • 01 для 62
    • 10 для 63
  2. битовая последовательность продолжает кодироваться стандартным образом

Заметим, что данным образом невозможно получить поседовательность ZZ, т.к. символ Z требует двух старших битов выставленных в 11

Пример преобразования:

Mshift = 5
bits = 1 01111 00 1 00010

Или же

bits = 101111 001000 10

Биты читаются младшим битом вперёд. Первые 6 бит:

101111 = 1*1 + 0*2 + 1*4 + 1*8 + 1*16 + 1*32 = 61 = Z

Тогда в последовательность добавляется два бита 00 сразу после битов, кодирующих Z.

bits = 101111 000010 0010
000010 = 16 = g
0010(00) = 4 = 4

Итоговая строка в base62:

10111100100010 = Zg4

Сравнение set-строк

При сравнении set-строк происходит обратный процесс преобразования до получений хэш-значений

set-строка
  | (обратное base62 преобразование)
  v
битовый массив
  | (обратное Rice-Golomb преобразование)
  v
массив delta
  | (вычисление изначальных значений)
  v
массив хэшей

После массивы хэшей сравниваются между собой на наличие жлементов одного массива в другом.

Комментарии

additional

коды от Арсения для наглядности происходящего

some funny msg's

по коду неоднократно раскидано bpp < 10 || bpp > 32 проверки

запись char = [1,0] мне не нравится.

про "отрицательные значения = код ошибки" встречается в многих местах, стоит вынести отдельно проверить кодом примеры, особенно base62