diff --git a/Docs/set:version.md b/Docs/set:version.md index e14a143..fccc74c 100644 --- a/Docs/set:version.md +++ b/Docs/set:version.md @@ -2,9 +2,12 @@ what is happening inside [set:version](https://git.altlinux.org/gears/r/rpm.git?a=blob;f=lib/set.c) +Актуальный код с наработками расположен в [репозитории](https://github.com/kr0sh512/alt-rpm-set-version.git) + ## Зачем set:version в alt-rpm позволяет сопоставлять Provides и Requires пакетов не обычным сравнением версий, а сравнением специальных set-строк зависимостей вида + ```text libfoo.so.X = set: ``` @@ -28,14 +31,14 @@ struct set *set_free(struct set *set); ### `rpmsetcmp()` -Основная функция, сравнивающая строки и выдающая результат в зависимости от включения: +Основная функция, [сравнивающая строки](#сравнение-set-строк) и выдающая результат в зависимости от включения: -* 1: set1 > set2 -* 0: set1 == set2 -* -1: set1 < set2 (aka set1 \subset set2) -* -2: set1 != set2 -* -3: set1 decoder error -* -4: set2 decoder error +- 1: set1 > set2 +- 0: set1 == set2 +- -1: set1 < set2 (aka set1 \subset set2) +- -2: set1 != set2 +- -3: set1 decoder error +- -4: set2 decoder error на основе [данной](https://github.com/svpv/rpmss/blob/4256d86cc9ba1aa4ceb8c0f03f7d48675d9d27bb/set.h#L12) заметки, `set1` лучше делать как `Provides` для лучшей производительности. @@ -45,6 +48,7 @@ struct set *set_free(struct set *set); Внутри `struct set` — это временный контейнер для строк символов и их будущих hash-значений. Из релизации: + > internally struct set is just a bag of strings and their hash values. ### `set_add()` @@ -52,6 +56,7 @@ struct set *set_free(struct set *set); Добавляет строковый символ в set. Использование: + ```c set_add(s, "printf@@GLIBC_2.2.5"); set_add(s, "malloc@@GLIBC_2.2.5"); @@ -61,28 +66,162 @@ set_add(s, "malloc@@GLIBC_2.2.5"); Финализирует множество и возвращает готовую set-version строку. -Внутри происходит основная работа: -1. Jenkins hash -2. обрезка до bpp бит -3. сортировка -4. delta кодирование -5. golomb кодирование -6. base62(64) кодирование +В качестве параметров принимает `struct set *set` и `int bpp` +При `bpp` < 10 или `bpp` > 32 функция возвращает `NULL`. + +Работа функции [описана далее](#работа-внутри-set_fini). ### `set_free()` Освобождает struct set и его внутренние строки. -## `set.c` под капотом +!!! 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` бит + +```c +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 -### golomb +Внутри функции `encode_delta()` происходит преобразование массива отсортированных по возрастанию чисел в массив дельт между числами, т.е. +Пример исходного массива: + +```c +unsigned *v = {1, 4, 16, 22}; +``` + +Пример результирующего массива: + +```c +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 @@ -90,3 +229,10 @@ set_add(s, "malloc@@GLIBC_2.2.5"); [коды](https://altlinux.space/arseny/atsv-research) от Арсения для наглядности происходящего some funny [msg's](https://lists.pld-linux.org/mailman/pipermail/pld-devel-en/2013-November/012467.html) + +по коду неоднократно раскидано `bpp < 10 || bpp > 32` проверки + +запись `char = [1,0]` мне не нравится. + +про "отрицательные значения = код ошибки" встречается в многих местах, стоит вынести отдельно +проверить кодом примеры, особенно base62