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