Сколько раз при изобретении очередного метода обработки структурированных данных наталкиваешься на мысль о дежавю? Работа со списками файлов, словарями имен, объектными полями, связывание разнотипных данных. В каждом новом более удобном или более быстром переизобретении проглядывается что-то общее, непреходящее. Концептуальное ядро, связующее все возможные производные множества и включающее их в свою орбиту. Что-то чему язык затрудняется сходу подобрать название, а мозг очертить предельные границы. Одновременно всеобъемлющая и при этом неуловимо малая деталь. Абсолютная абстракция. Линейный примитив. Лень — двигатель прогресса. Парадоксальное утверждение, частично приоткрывающее завесу тайны над тем, каким образом достигается высокоэффективный результат. Кто‑то скажет: механизмы, разработанные на высокоуровневом языке (типа С) обладают лаконичностью при написании рутинного кода, кроссплатформенностью, высокой степенью простоты и понятности и даже непревзойденной оптимизацией (при известных ограничениях). И обязательно найдутся те, кто возразят, что концептуальные разработки, внедрение инноваций и экзистенциальные изыскания за пределами неизвестного — это без всяких сомнений самое низкоуровневое программирование. Только ассемблер, только неограниченный доступ, только хардкор. Но я лично одно без другого не представляю. Задействовать Intrinsic обрабатывая дробные данные в регистре и при этом экономить обращения к памяти, вычленять из последовательности бит с помощью маски нужные значения векторной проекции и оценивать относительную разницу с предыдущим состоянием. Одна мысль об этом способна породить непреодолимую депрессию. Поэтому мой подход‑ это pure C для рутинных процедур, большей частью выполняющихся при загрузке программы и ассемблер для высокоприоритетной real‑time механики. Минимум затраченных усилий при максимальной функциональности — тот компромисс, который напрашивается естественным образом, когда не хочешь разрывать свой мозг пополам, пытаясь определиться умная ты обезьяна или все‑таки красивая. Как связать разнородные данные в большую кучу и найти потом то, что привязано к нужному объекту? Каждую запись, каждую ячейку, каждое поле? А если данные имеют некоторую динамику и внушительные пределы? Чаще всего нет возможности создать структуру из предельного множества полей для всех объектов, если реально каждый их них будет использовать лишь малую часть. Также ограничение в ресурсах (какими бы они ни были) вступает в противоречие с жесткостью структуры. Линейный массив это быстрое и простое решение, но для динамических данных необоснованно дорогостоящее. Как создать универсальный концепт, сочетающий плюсы отдельных решений и избежавший их минусов. Способный и размещать данные линейно и сохранять их непоследовательными порциями, растянутыми во времени? Универсальный как идеальный самурай — неотразимый, беспринципный, лишенный эмоций, элементов персональности (даже волос) и потому неуловимый. Достигший в стихийных непредсказуемых обстоятельствах высокой степени непрерывности. Мой подход — использовать и массивы и цепочки указателей. Ограничения, как и итоговую структуру, должен выстраивать сам программист. Концепт при этом его ни в чем не ограничивает. Реализация представляет из себя два метода: секвенирование цепочек указателей и рекомбинация цепочек указателей. Каждый указатель ведет к некоему линейному массиву. Размер массива произвольный, указывается параметром flexiSize (пластичный размер). Ниже приведенная реализация использует 64бит указатели и 32бит смещения. Но концепт никак не мешает реализовать другие версии. Входящий параметр memPtr - это указатель на диапазон памяти, где будет размещаться создаваемая структура. В данной вариации используется возможность передать большой буфер, который постепенно будет размечать пользовательская процедура memoryArbitrary. nullPoint - это точка нуля, от которой будет происходить поиск. Для тех кто не знает, что точкой нуля может быть какая угодно точка, приведу аналогию. В реализации работы с коллекцией файлов очень удобно каждой следующей точкой нуля назначать нижележащий каталог. Это как namespace, позволяет избежать повторного прохождения уже прочитанных данных. stringAddr - это указатель на путь. Процедура работает с путем как массивом char, но по большому счету путь это просто последовательность бит. Данная вариация делает 8битные шаги по указанному пути. Но у меня есть и другие вариации. Путем может быть как ассоциативный токен, так и что-нибудь нечленораздельное. flexiSize - это структура, содержащая размеры. Нижние 32бит содержат количество шагов в пути. Верхние 32бит содержат байтовый размер линейного массива, присоединенного к указателю. Создать тестовую структуру предельно просто. Для начала понадобится лишь формальное определение структуры (а можно и без него) и точка нуля. Точка нуля это произвольный обнуленный отрезок памяти, выделенный пользовательской процедурой (malloc или allocate for mdl или что-нибудь еще). Как видно в структуре сами связи представляют из себя 4 блока links. Всё остальное это присоединенный линейный массив, который можно настроить на свой манер. Нетрудно догадаться, что простое структурирование данных по типу дерева каталогов приведет к структурированию по типу дерева указателей. Возможность управлять точкой нуля наподобие namespace это конечно хорошо, но как быть с произвольными запросами глубоко вложенных данных? В СУБД есть возможность отсортировать массивы по нужному признаку, сформировав последовательность цепочки нужным образом. Опрометчиво было бы игнорировать подобную необходимость. Реализация альтернативных путей (или точек входа) для линейного массива данных обеспечивается рекомбинацией указателей. Процедура рекомбинации в целом повторяет секвенирование, с той лишь разницей, что указатель на последний элемент (целевые данные) не возвращается, а перезаписывается на перенаправляемые данные. Параметр target - это данные, на которые будет указывать новый путь. nullPoint - точка начала поиска, stringPtr - путь, size - количество шагов в пути. Вот создание двух альтернативных путей для одного массива данных. В первой строке создаем некий массив размером 16 байт. Во второй создаем еще один, с более коротким путем и без присоединенных данных. В третьей заменяем конечную точку короткого пути на ту, куда указывает длинный. Теперь на один и тот же массив данных указывают два пути: короткий и длинный. Если подобные структуры размещать во временном буфере (например списки файлов при синхронизации двух каталогов), то после отработки основных действий никакой утилизации не требуется. Я лично использовал подобную технику в фильтр‑драйвере файловой системы при разработке сервиса подобного облаку oneDrive, но с хранилищем данных внутри локальной сети. Для синхронизации с основной структуры создавалась такая копия в виде расслоения. Часть расчетов велась на локальном диске со своей структурой каталогов, часть на сетевом со своей. Внутренняя же часть (совпадающая) в виде наложенного слоя размещенная в отдельном временном буфере переносилась с одной структуры на другую. Это позволило увидеть в локальной папке файлы, которых реально на локальном диске не было и которые были размещены на сетевом хранилище. Все необходимые поля были скопированы из структуры и использованы в драйвере при построении списка. При первом обращении к файлу тут же начиналось копирование. Я конечно не знаю, как реализован OneDrive от Microsoft, но зная как в принципе реализуются различные драйвера Microsoft, подозреваю, что это очень громоздкая механика размером гораздо более тех 100 кб, что понадобились мне. При закрытии папки с файлами никаких утилизационных действий не требуется. Буфер не очищается, потому‑что он многоразовый и используется по принципу «мыть непосредственно перед едой». Рекомбинация достаточно универсальный метод и позволяет не только создать множественные точки входа, но и строить гораздо более сложные интегральные схемы с рекурсией, прыжками и ветвлением. Для того чтобы сразу показать более продвинутые возможности абстрактного структуризатора можно испробовать его в семантических механиках. Поскольку это тема для отдельного серьезного исследования, я ограничусь реализацией сферического (циклического) парсера в вакууме (в оторванном от реальности примере) и реализую обработчик, который читает строку со случайным текстом и разбирает ее на токены. Настройка интегральных схем на pure c, обработчик на ассемблере.  Для ассемблерного обработчика скриптовой строки используется ассемблерный же секвенсер. Он шаговый, т.к. чтение и обработка строки посимвольные. Реализована возможность записи новых цепочек, которая в данном примере не используется. memoryArbitrary — такая же пользовательская процедура выдачи памяти, buffer — такой же произвольный буфер. Чтение одного шага — это также 4х кратное обращение к оперативной памяти как в С варианте.  Обработчик Splitter (делитель строки на токены) обходит посимвольно скрипт (цикл action), считывает интегральную схему, в которую внесен весь алфавит символов. Прерываясь после каждого отрезка, выделяющего токен, получает доступ к связанным данным. Потенциально может выполнять дальнейшие действия с токенами, основываясь на полученной информации (цикл transaction). Формальная система выносится за скобки и содержится в дифференциальной сети. В ней используются прыжки, рекурсии и ветвления. В обработчике нет правил обработки строк, а формальная система не содержит инструкций. Весь процесс разделения строится исключительно за счет организации связанных данных. Это легко можно понять, если представить, что строка (поток бит) — это волна. Формальная система — частотный модулятор. Считывая в обработчике по одному знаку, мы отправляем его в дифференциальную сеть и получаем из нее связанные данные. Таким образом полученный фрагмент строки становится несущей волной. Информация, которую можно извлечь из частотной дифференциации также абстрактна и несет смысл исключительно в контексте потока. Главное, что с ее помощью можно объединить некоторое множество символов, а поток соответственно разделить на отрезки. Придумаем несколько отличных друг от друга видов привязанных данных, пусть это будут просто последовательные битовые смещения. Дадим разным смещениям осмысленные названия, исключительно для собственного удобства. Чтобы не запутаться какие множества символов с какими связанными данными будут группироваться. Таким образом получаем как бы типы для этих отрезков (или типы данных), которые не несут никакой функциональной нагрузки, кроме их явного отделения друг от друга. Далее можно загрузить алфавит в дифференциальную сеть (столько раз, сколько различных контекстов будет существовать для каждого отдельного знака). Каждый символ алфавита это путь от точки нуля, указующий на линейный массив. В нем размещаются привязанные данные (тип текущего символа в текущем контексте). Придумаем несколько самых распространенных контекстов — идентификатор, десятичное число, шестнадцатеричное число, неопределенное число и один общий контекст разнотипных данных. Сделаем два разнотипных контекста — четный и нечетный, для тех случаев когда последовательно в одном отрезке мы можем встретить несколько символов к примеру скобок «(» и «)», но при этом объединять в один токен их будет неправильно, ведь это несколько разных токенов. Реализация на С, настраивается один раз при старте процесса.  Загружая символы в дифференциальную сеть, продвигаемся от точки нуля к следующей. Сохраняем новую точку, она будет следующей точкой нуля. Получая однотипные символы из строки мы продвигаемся в одном и том же контексте. Но есть указатели и на другие контексты. Получив например цифру после пробела мы получим указатель на другой контекст. Символы в нем повторяются, но несут в себе другие типы данных. Таким образом у каждого контекста есть точки входа и выхода для перемещения между разными контекстами. Разнотипный контекст при этом как бы центральный и разветвляется во все остальные контексты. А все остальные контексты в свою очередь выходят в разнотипный. Контекст неопределенных чисел общий для частных контекстов десятичного числа и шестнадцатеричного. Это только основные контексты, вообще их можно придумать гораздо больше: цитаты, комментарии, деклассифицированные данные и т.д. При помощи приведенной выше формальной системы можно прочитать скрипт для исполнения такого вида: Код запуска на С самый простой. В обработчик передается указатель на скрипт (текстовая строка char), указатель на точку нуля контекстной формальной системы (tmFirst), точка нуля высокоуровневого контекста (tmSecond - здесь не рассматривается) и указатель на произвольный стек, для возможных стековых операций. Обработчик выделяет каждый токен, при выделении отрезка переходит в блок transaction, где в специальном регистре доступен тип токена и может предпринимать дальнейшие высокоуровневые действия. Внимательный читатель заметил, что пример был выдран из работающей стековой машины, исполняющей скрипты с классическим синтаксисом С. Нюансы реализации намеренно опущены, они не относятся к теме дифференциальных сетей и по объему гораздо больше, чем всё выше написанное. Цель примера показать, как дифференциальные сети способны структурировать самые разные типы данных.