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
|
- 1: set1 > set2
|
||||||
- 0: set1 == set2
|
- 0: set1 == set2
|
||||||
- -1: set1 < set2 (aka set1 \subset set2)
|
- -1: set1 < set2
|
||||||
- -2: set1 != set2
|
- -2: set1 != set2
|
||||||
- -3: set1 decoder error
|
- -3: set1 decoder error
|
||||||
- -4: set2 decoder error
|
- -4: set2 decoder error
|
||||||
@@ -44,9 +44,7 @@ struct set *set_free(struct set *set);
|
|||||||
|
|
||||||
### `set_new()`
|
### `set_new()`
|
||||||
|
|
||||||
Создаёт пустой объект `struct 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.
|
||||||
@@ -102,9 +100,10 @@ set-строка
|
|||||||
|
|
||||||
### hash
|
### hash
|
||||||
|
|
||||||
Для каждого элемента (строки) высчитывается 32-битный хэш и обрезается до `bpp` бит
|
Для каждого элемента (строки) высчитывается 32-битный хэш и обрезается до `bpp` младщих бит
|
||||||
|
|
||||||
```c
|
```c
|
||||||
|
unsigned mask = (1u << bpp) - 1;
|
||||||
set->sv[i].v = hash(set->sv[i].s) & mask;
|
set->sv[i].v = hash(set->sv[i].s) & mask;
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -135,7 +134,7 @@ unsigned *v = {1, 3, 12, 8};
|
|||||||
|
|
||||||
### Rice-Golomb
|
### 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` записанный стандартным двоичным кодированием. Целая часть и остаток разделяется единичном битом.
|
Для сокращения длины итоговой строки применяется 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
|
проверить кодом примеры, особенно base62
|
||||||
|
|
||||||
|
кэш вечно копируется и переносится, немного странно
|
||||||
|
PIVOT_SIZE странный
|
||||||
|
|
||||||
|
прыжки IFLT8 и IFLT4 я бы возможно делал как c1/c2
|
||||||
|
|
||||||
|
учитывая оптимизации, возможно стоит самостоятельно менять массивы местами до начала всех операций (но проблема с кэшем возможна)
|
||||||
|
|||||||
@@ -24,3 +24,15 @@ xxHash64 (XXH64): относительно простая имплементац
|
|||||||
|
|
||||||
в случае XXH64 строк кода ~200, можно вставить в файл
|
в случае XXH64 строк кода ~200, можно вставить в файл
|
||||||
в случае XXH3 строк кода ~2к, стоит использовать библиотеку напрямую, бессмысленно
|
в случае 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