describe decode_set(). hell.

This commit is contained in:
2026-07-05 20:56:08 +03:00
parent b44fe3849e
commit 02eb7d5034
+111 -4
View File
@@ -167,7 +167,7 @@ bits = 00 1 00101010
- `10` для 63
2. битовая последовательность продолжает кодироваться стандартным образом
Заметим, что данным образом невозможно получить поседовательность `ZZ`, т.к. символ `Z` требует двух старших битов выставленных в `11`
Заметим, что данным образом невозможно получить поседовательность `ZZ`, т.к. символ `Z` требует двух старших бит выставленных в `11`
Пример преобразования:
@@ -188,7 +188,7 @@ bits = 101111 001000 10
101111 = 1*1 + 0*2 + 1*4 + 1*8 + 1*16 + 1*32 = 61 = Z
```
Тогда в последовательность добавляется два бита `00` сразу после битов, кодирующих Z.
Тогда в последовательность добавляется два бита `00` сразу после бит, кодирующих Z.
```
bits = 101111 000010 0010
@@ -238,6 +238,111 @@ set-строка
### `decode_set()`
Под `if(0)` в функции `decode_set()` описано интуитивное преобразование с помощью функций `decode_base62()`, `decode_golomb()` и `decode_delta()`.
На деле же используется оптимизированный вариант из `decode_base62_golomb()` и `decode_delta()`, благодаря которым set-строка сразу преобразуется в массив `delta` значений и после восстанавливается до массива хэшей.
### `decode_base62_golomb()`
`decode_base62_golomb()` - оптимизированная версия стадий `decode_base62` и `decode_golomb`. Функция считывает сразу по два байта, пользуясь таблицей `word_to_num` преобразовывает их в битовую последовательность, после, набирая до 24х байт, декодирует по Rice-Golomb.
#### `enum`
> `enum` - word types (when two bytes from base62 string cast to unsigned short).
В коде используется `enum` для обозначения особых случаев при считывании символов:
```c
enum {
W_AA = 0x0000, // два обычных символа (явно не используется)
W_AZ = 0x1000, // обычный символ + Z
W_ZA = 0x2000, // Z + обычный символ
W_A0 = 0x3000, // обычный символ + конец строки
W_0X = 0x4000, // конец строки
W_EE = 0xeeee, // невозможная ситуация
};
```
#### `CCI` macros
`CCI` - макрос, объединяющий два символа в индекс таблицы `word_to_num`, с учётом порядка байтов
#### `word_to_num[]`
```c
static const unsigned short word_to_num[65536];
```
pre-compiled таблица соответствия любой комбинации из двух байт, битовому представлению из 12 символов + возможный флаг
Таблица строится следующим образом:
1. все значения заполняются `W_EE` как ошибоные
2. с помощью макроса `AA1` строятся макросы `AA1x2`, `AA1x25` и т.д. вплоть до `AA10x10` и ему аналогичных.
- итоговые макросы позволяют быстро заполнить таблицу значениями `[CCI(c1, c2)] = (c1 - b1) | ((c2 - b2) << 6)`
3. аналогичный процесс заполнения для `AZ`, но с добавлением флага `W_AZ` к значениям в таблице.
4. заполняется таблица для `ZA` значений с флагом `W_ZA`. Невозможности старших бит `11` во втором символе (т.е. символы с 48 номера) проверяется лишь внутри функций, `W_EE` для таких значений не возвращается
5. заполняется таблица для `A0` и `0X` значений с флагами `W_A0` и `W_0X` соответственно.
#### Считывание
Функция считывает с помощью макросов `GetXX` несколько бит (стандартно блоками до 24х бит) и передаёт их на декодирование
при 24 битах:
- берутся именно 4 base62-символа (по два пары)
- значение помещается в 32-битный `unsigned`
- при ограничении `Mshift >= 7` в одну последовательность может поместиться максимум 3 закодированных golomb числа.
"12 бит" считыватель используется в ситуациях, когда первая пара обычная, а вторая содержит специальный случай
"10 бит" считыватель используется в ситуциях с Z-escape
"6 бит" считыватель используется для одного символа.
#### Rice-Golomb декодер
Декодер постоянно находится в двух состояних:
- `q-state`: ищет unary-префикс и разделительную `1`
- `r-state`: добирает Mshift бит остатка `r`
декодированное число восстанавливается как
```c
value = (q << Mshift) | r;
```
#### Обработка Z-escape
Для обработки Z-escape используются макросы `Esc1` и `Esc2`.
`Esc1` вызывается при обнаружении `Z` символа и считывает старшие 2 бита у следующего символа. В результате `Esc1` получит 10 бит для golumb-декодера
`Esc2` вызывается после завершения escape-пары и решает что делать дальше:
- обычный символ — обработать 6 бит
- конец строки — завершить декодирование
- недопустимый символ — вернуть ошибку
- новый `Z` — снова перейти в `Esc1`
#### `QMake` и `RMake` макросы
`QMake` ищет разделительную единицу в текущем блоке бит. Если блок содержит только нули, все они добавляются к q, после чего функция читает следующий блок.
Если в блоке есть единица, используется `__builtin_ffs(bits)` для определеия позиции первого бита, разделительной для golomb-кода. Оставшиеся биты заполняют r.
`RMake` проверяет, набралось ли `Mshift` бит остатка, записывает готовое значение
```c
*v++ = (q << Mshift) | r;
```
#### Завершение строки
Допустимо завершение строки в `q-state`, но с не более чем 5 нулями. Завершение в `r-state` невозможно, т.к. остаток длиной `Mshift` не набрался.
### `cache_decode_set()`
> `cache_decode_set()` - special decode_set version with LRU caching.
@@ -271,7 +376,7 @@ unsigned hash = str[0] | (str[2] << 8) | (str[3] << 16);
### `downsample_set()`
> `downsample_set()` - Reduce a set of (bpp + 1) values to a set of bpp values
> `downsample_set()` - reduce a set of (bpp + 1) values to a set of bpp values
Входной массив для работы - `v`.
Итоговый массив будет доступен по указателю `w`.
@@ -357,7 +462,7 @@ decode_golomb()
### `test_word_table()`
WIP
В таблице `word_to_num[65536]`, необходимой для функции `decode_base62_golomb()`, проверяет, чтобы последовательность `AA` (двух не escape-символов) была равна `(char_to_num[i] | (char_to_num[j] << 6)`. Для всех остальных ситуаций проверяется лишь значение одного из символов большее 61 `char_to_num[i] >= 61 || char_to_num[j] >= 61`.
### `test_base62_golomb()`
@@ -426,3 +531,5 @@ PIVOT_SIZE странный
прыжки IFLT8 и IFLT4 я бы возможно делал как c1/c2
учитывая оптимизации, возможно стоит самостоятельно менять массивы местами до начала всех операций (но проблема с кэшем возможна)
хочу себе день, чтобы переписать это всё на English