Ладно.
О терминах:
Active-Range — диапазон допустимых символов в слове.
LFT — Length-Filtering Table (частный случай JAT).
JAT — Jump Adresses Table.
При финализации библиотеки (все опции выставили, вызвали функцию Finalize) в памяти создаётся базовая, самая главная и первичная таблица — LFT.
Функция [GetWordID] делает элементарное:
Берёт длину слова. Умножает её на 4. Прибавляет результат к указателю LFT. И делает jmp по полученному адресу.
Изначально, когда не добавлено ещё ни одного слова, все ячейки LFT содержат указатели на одно и то же — на терминаторы.
Терминатором называется некий кусочек кода, примерно такого содержания:
xor eax, eax
retn ???
Поэтому, если, не добавив предварительно никаких слов, вызвать [GetWordID] с каким-нибудь словом, будет сделан jump прямо на терминатор, т.е. будет возвращён 0, что означает, что слово не распознано.
При добавлении "в систему" нового слова для его обработки строится дерево. Я называю его деревом маршрутизации. Посколько в данном механизме слово является не более чем маршрутом, по которому пройдётся процессор, выполняя код роутеров.
Роутер (в рамках данной библиотеки), это код, который берёт текущий символ слова, умножает его код на 4, прибавляет к адресу ассоциированной с роутером JAT и делает по полученному адресу jmp.
Частично дерево уже может быть построено, поэтому происходит маршрутизация (только в данном случае обход дерева осуществляет не сам процессор, выполняя код роутеров и производя джампы, а код библиотеки, изучая JAT-ы), определяется то место слова, на котором дерево обрывается и оставшаяся часть дерева достраивается. Листками дерева (узлами без потомков) являются финализаторы — кусочки кода, отличающиеся от терминаторов только тем, что не обнуляют eax, а записывают в него идентификатор слова.
Для примера представим, что алфавит у нас 5-буквенный, а слова могут быть длиной от 1 до 4 букв.
Слова следующие:
- Код: Выделить всё
A — 1
B — 2
E — 3
BAA — 4
BAB — 5
BAE — 6
CEC — 7
CED — 8
ABAD — 10
ABAE — 11
ABBA — 12
После добавления всех этих слов дерево будет выглядеть так:
Получается, что для того, чтобы распознать слово длиной L символов должно выполниться 4+6L инструкций процессора (в предлагаемой библе несколько больше). Что очень мало для такой задачи, а значит код будет выполняться очень быстро. Кроме того, ты данные из памяти читают блоками (ты сам говорил) и кешируются.
В предлагаемой библе всё монолитно. А так, я отсоединил механизм создания самого графа маршрутизации (теперь он может быть сколько угодно зацикленным, содержать JAT-ы любого размера, несколько ребер-переходов может вести к одному роутеру, а в качестве роутера может быть произвольный код) и оформил его в виде отдельной библы. Задача распознования слова построена над этой библой. Кроме того, на этой библе я уже сделал несколько парсеров, а также
упомянутый ранее дизассемблер.