describe decode helper func

This commit is contained in:
2026-07-04 16:52:13 +03:00
parent 2bc3fe30bf
commit 0bf78d4c68
2 changed files with 133 additions and 7 deletions
+121 -7
View File
@@ -35,7 +35,7 @@ struct set *set_free(struct set *set);
- 1: set1 > set2
- 0: set1 == set2
- -1: set1 < set2 (aka set1 \subset set2)
- -1: set1 < set2
- -2: set1 != set2
- -3: set1 decoder error
- -4: set2 decoder error
@@ -44,9 +44,7 @@ struct set *set_free(struct set *set);
### `set_new()`
Создаёт пустой объект `struct set`.
Внутри `struct set` — это временный контейнер для строк символов и их будущих hash-значений.
Создаёт пустой объект `struct set` — контейнер для строк символов и их будущих hash-значений.
Из релизации:
> internally struct set is just a bag of strings and their hash values.
@@ -102,9 +100,10 @@ set-строка
### hash
Для каждого элемента (строки) высчитывается 32-битный хэш и обрезается до `bpp` бит
Для каждого элемента (строки) высчитывается 32-битный хэш и обрезается до `bpp` младщих бит
```c
unsigned mask = (1u << bpp) - 1;
set->sv[i].v = hash(set->sv[i].s) & mask;
```
@@ -135,7 +134,7 @@ unsigned *v = {1, 3, 12, 8};
### Rice-Golomb
> `encode_golomb()` - Main golomb encoding routine: package integers into bits.
> `encode_golomb()` - main golomb encoding routine: package integers into bits.
Для сокращения длины итоговой строки применяется Rice-Golomb кодирование, которое позволяет с параметром `Mshift` записать число `n` как `n >> Mshift`, записанный последовательностью нулей и `n & 2^Mshift` записанный стандартным двоичным кодированием. Целая часть и остаток разделяется единичном битом.
@@ -220,7 +219,115 @@ set-строка
массив хэшей
```
После массивы хэшей сравниваются между собой на наличие жлементов одного массива в другом.
После массивы хэшей сравниваются между собой на наличие элементов одного массива в другом.
## Магия внутри `rpmsetcmp()`
1. У строки обрезается префикс `set:`, если он присутствует, декодируется значение `bpp` и `Mshift` с помощью `decode_set_init()`
2. `set1` декодируется с помощью `cache_decode_set()`
3. `set2` декодируется с помощью `decode_set()`
4. для каждого массива хэшей (`v1` и `v2`) создаётся два буферных массива `v(1|2)buf(A|B)` для функции `downsample_set()`
5. С помощью функции `downsample_set()` `bpp` обоих хэшей выравнивается до минимального из `bpp1` и `bpp2`.
6. Создаётся два флага `ge` и `le`, для определения вложенности множеств
7. Вложенность множеств проверяется с помощью 3х макросов: `IFGE` `IFLT4` `IFLT8`
8. Возвращается значение в зависимости от вложенности:
- 1: set1 > set2
- 0: set1 == set2
- -1: set1 < set2
- -2: set1 != set2
### `decode_set()`
### `cache_decode_set()`
> `cache_decode_set()` - special decode_set version with LRU caching.
Возвращает количество элементов хэша в массиве, помещает указатель `*pv` на массив хэшей.
Внутри `cache_decode_set()` создаются статические массивы:
- `static unsigned hv[CACHE_SIZE]` - для хранения fingerprint set-строки
- `static struct cache_ent *ev[CACHE_SIZE]` - для хранения полной информации о декодированной строке
В качестве fingerprint используется простой и быстро вычисляемый
```c
unsigned hash = str[0] | (str[2] << 8) | (str[3] << 16);
```
массивы `hv` и `ev` представляют из себя аналог LRU кэша размером `CACHE_SIZE=256`.
Если fingerprint set-строки на декодирование совпал с имеющимся в `hv` массиве, проверяется полное соответствие строки с `ev[i]->str`. В случае hit - элемент помещается на нулевую позицию, остальные элементы сдвигаются.
При неудачной проверке полной set-строки, поиск по кэшу продолжается.
В случае, если элемента не оказалось в кэше, строка декодируется функцией `decode_set()`, добавляется `SENTINELS=8` бит и помещается в кэш следующим образом:
- Если кэш не заполнен, декодируемая строка записывается на первую свободную позицию
- Если кэш заполнен, элемент ставится на `PIVOT_SIZE=243` позицию, элементы с этой позиции сдвигаются.
#### `SENTINELS` биты
Данные `~0u` биты необходимы при дальнейшем проходе по массиву внутри функции `rpmsetcmp()`, т.к. там происходят прыжки по 4 и 8 элементов.
### `downsample_set()`
> `downsample_set()` - Reduce a set of (bpp + 1) values to a set of bpp values
Входной массив для работы - `v`.
Итоговый массив будет доступен по указателю `w`.
Возвращаемое значение - количество элементов в новом массиве `w`.
Т.к. первоначальный входной массив отсортирован, после обрезания до `bpp` бит, массив будет поделён на две части, обе из которых будут отсортированны. Деление массива будет происходить в месте, где старший бит на позции `bpp+1` становится равным `1`.
Далее обе половины массива объединяются в буфере `w`, дубликаты значений удаляются.
Пример:
```text
v = [1, 3, 6, 8, 10, 14]
bpp = 3
mask = 7
v_mask = [1, 3, 6, 0, 2, 6]
w = [0, 1, 2, 3, 6]
return value = 5
```
### Макросы `IFGE` `IFLT4` `IFLT8`
#### Макросы `IFLT*`
`IFLT8` быстро продвигают `v1`, пока `*v1 < v2val`. Сперва макрос "грубо" прыгает по 8 элементов, далее уточняет с шагом 4, 2, 1.
```text
+8 +8 +8 ... // грубый поиск
-4 // откат
±2 // уточнение
±1 // уточнение
+1 возможно // финальная коррекция
```
В итоге `v1` оказывается на первом элементе, который не меньше `v2val`.
`IFLT4` делает аналогичную работу, но с шагом в `4`. `IFLT8` выбирается в случае, когда массив `v1` превосходит по размеру массив `v2` более чем в `16` раз.
#### Макрос `IFGE`
После `IFLT*` вариант `*v1 < v2val` уже невозможен.
Значит остаётся:
```c
*v1 > v2val
```
То есть текущий элемент `v2val` есть во втором множестве, но отсутствует в первом.
Следовательно `v1` уже не может быть над множеством `v2`:
```c
ge = 0;
v2++;
```
## Комментарии
@@ -236,3 +343,10 @@ some funny [msg's](https://lists.pld-linux.org/mailman/pipermail/pld-devel-en/20
про "отрицательные значения = код ошибки" встречается в многих местах, стоит вынести отдельно
проверить кодом примеры, особенно base62
кэш вечно копируется и переносится, немного странно
PIVOT_SIZE странный
прыжки IFLT8 и IFLT4 я бы возможно делал как c1/c2
учитывая оптимизации, возможно стоит самостоятельно менять массивы местами до начала всех операций (но проблема с кэшем возможна)