describe decode helper func
This commit is contained in:
+121
-7
@@ -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
|
||||
|
||||
учитывая оптимизации, возможно стоит самостоятельно менять массивы местами до начала всех операций (но проблема с кэшем возможна)
|
||||
|
||||
@@ -24,3 +24,15 @@ xxHash64 (XXH64): относительно простая имплементац
|
||||
|
||||
в случае XXH64 строк кода ~200, можно вставить в файл
|
||||
в случае XXH3 строк кода ~2к, стоит использовать библиотеку напрямую, бессмысленно
|
||||
|
||||
## about collizions
|
||||
|
||||
> А вы пробовали побитовую вероятностную карту строить?
|
||||
> Есть у нас по условию множество из входных значений, которые замапились в N хешей (включая коллизии!). Эти N хешей имеют одинаковый размер и, по сути, являются массивами из K бит.
|
||||
> Так вот можно среди этих хешей посчитать 0 и посчитать 1, получится K распределений. Чем они все ближе к пополамному ± 6%, тем лучше.
|
||||
> https://raw.altlinux.team/arseny/snippets/2026-06/QSIrJSuwxp.txt
|
||||
> https://raw.altlinux.team/arseny/snippets/2026-06/QSIrJSuwxp.c
|
||||
|
||||
## free
|
||||
|
||||
free не делает очистку самой структуры, valgrind
|
||||
|
||||
Reference in New Issue
Block a user