Конструктивной частью нашей (во многом, содержательной) дискуссии с Артемом я считаю, для себя, реализацию модифицированного модуля сереализации Lua-таблиц. Этот модуль создан в соответствии с вариантом решения, который был кратко описан мною в комментарии № 164.
Замечу, что мои комментарии в ходе дискуссии с Артемом, особенно в части размеров кода, относились к функциональности, выложенного им модуля. Функциональность модифицированного модуля, а также эффективность выполнения в нем сереализации и восстановления таблиц, иные.
---
Код модуля выложен в конце данного комментария.
-----
В простом варианте восстановления сереализованной таблицы (в том числе и после перезапуска скрипта), когда контекст (определение приведено в описании модуля) ее восстановления не отличается от контекста сереализации, все просто:
-- Сереализация таблицы --
<Строка образа (! в случае отсутствия 3-го параметра)> = tbl_to_str (<Таблица> [, <Путь файла сохранения образа или вид результата (0 | 1 байт-код или строка-скрипт)> [ ,<Контекст сереализации>]])
………… !! Здесь может быть и перезапуск скрипта --
-- Полное восстановление таблицы (со всеми ее значениями без исключения ) --
<Таблица)> = str_to_tbl (<Сохраненный где образ - результат функции tbl_to_str > [,<Контекст сереализации>])
------
Более сложный вариант использования модуля (при различии контекста восстановления таблицы от контекста сереализации) приведен далее, в его описа-нии.
--------------------------------------------
В описании модуля значения типов function, userdata и thread называются ссылочными значениями.
Сереализуемая таблица, в описании, обозначена именем <T>.
Внутренним именем ссылочного значения называется результат функции internal_name (<Cсылочное значение>), определенной в модуле.
Составное имя имеет вид адреса поля в таблице: <T1>.<T2>…..<Tn>.<Поле таблицы Tn>
Под контекстом выполнения (сереализации, восстановления) далее описываемых функций понимается список доступных им, в момент их выполнения, ссылочных значений, на которые есть ссылки из <T>.
--
Описание модуля.
1. Существенных отличий данного модуля, от выложенного Артемом, три:
1) Формирование таблицы ссылочных значений <T> в формате, обеспечивающем, полное восстановление <T> (в том числе, с учетом ее локаль-ного окружения) в контексте ее сереализации из образа, сохраненного в файле (в том числе и после перезапуска скрипта). Кроме того, обеспечен удобный контроль и коррекция результата восстановления <T> в контексте, отличном от контекста ее сереализации.
2) Результатом сереализации таблицы <T> является Lua-скрипт восстановления этой таблицы (в виде байт-кода или исходного кода Lua), со-держащий в себе две таблицы ссылочных значений (контекста и <T>),
3) На таблице _G (в моем скрипте) время выполнения сереализации в дан-ном модуле в ~1,5 раза меньше, чем в сравниваемом модуле, а время восстановления _G меньше в ~ в восемьнадцать раз (мои конкретные числа данного модуля для _G ~ 8000 элементов: сереализация ~ 55 млсек., восстановление ~ 2 млсек.). С уменьшением сереализуемой таб-лицы, описанные выше времена, уменьшаются почти линейно.
2. В модуле доступны пять функций:
1) Сереализация таблицы:
tbl_to_str (<T> [, <Признак выдачи результата в виде байт-кода или строки-скрипта 0|1> | <Путь файла записи строки-скрипта > [ ,<Контекст сереализации: таблица или функция >] ])
Результат в зависимости от входных параметров: <байт-код восстановления <T> | <Строка-скрипт восстановления <T>> | <Запись строки-скрипта в файл с заданным путем>.
2) Восстановление таблицы:
str_to_tbl (< Байт-код скрипта для восстановления <T>, либо строка-скрипт>[ , <Контекст восстановления: таблица или функция >])
Результаты. Их может быть один или три (если есть висячие ссылочные значения):
1-й - восстановленная таблица с пометкой висячих ссылок, если такие существуют в виде составных имен в контексте сереализации.
Второй и третий – nil, если висячих ссылок нет или две таблицы:
- таблица висячих ссылок с указанием составных имен-адресов в кон-тексте сереализации;
- таблица висячих ссылок с указанием составных имен –адресов в <T>.
Эти данные могут быть основой для восстановление таких ссылок.
3) Формирование служебных таблиц, используемых в функциях tbl_to_str и str_to_tbl:
creating_link_tables (<Контекст выполнения функций tbl_to_str и str_to_tbl > [, <Признак обновления существующего контекста = 1>])
Первый параметр либо таблица (например, _G), либо пользователь-ская функция создания таблиц контекста (по образцу аналогично тому, как это делается в creating_link_tables). В случае функции, при создании | изменении контекста, вместо creating_link_tables запускается пользовательская функция. По умолчанию контекстом является _G.
Результаты функции две таблицы формируемые в ней.
4) Сбор (быстрый) строки из массива строк:
m_str_to_str_fast ( <Таблица-массив строк> [, <Признак последовательности сбора (false – прямой; true – обратный) >] ).
5) Печать любой таблицы Lua в виде удобном для анализа, с указанием уровней вложенности всех ее элементов:
dump_tbl (<Таблица>, <Символ отступов для выделения вложенности эле-ментов таблицы>, <Уровень раскрываемых вложенностей таблицы (если 0, то показывается все > [, <Вид выдачи результата: false – таблица (по умол-чанию); true - строка> [, < Таблица имен элементов (задаваемых в ключах) , не разворачиваемых далее >]]).
----
В квадратных скобках при описании всех функций указаны необязательные параметры.
3. При сереализации <T> ее ссылочные значения не сохраняются, но при восстановлении <T>, ссылки на ее ссылочные значения, разрешаются (устанавливают-ся на значения) в контексте ее восстановления. Если контекст, в момент восстановления <T>, такой же (в части всех ее ссылочных значений), как и в момент ее сереализации, то ничего кроме запуска функции str_to_tbl с единственным параметром-результатом, полученным из функции tbl_to_str не требуется; в этом случае таблица <T> будет восстановлена полностью (со всеми ее значениями, включая ссылочные).
При отличии контекста восстановления от контекста сереализации таблицы в момент ее восстановления, некоторые ссылки могут оказаться «висячими». Для возможности восстановления висячих ссылок функция str_to_tbl выдает дополнительно к восстановленной <T> c указанием в ней висячих ссылок, две таблицы описания таких ссылок в удобном для анализа виде.
4. В функции str_to_tbl выполняется автоматический контроль разрешимости ссылок <T> на ее ссылочные значения. Выполняемый контроль частичный, в том смысле, что если он не проходит, то это значит, что контекст восстановления <T> точно отличен от контекста ее сереализации. Если же этот контроль проходит, то это означает, что имеется всего лишь полное совпадение сформированных имен ссылочных значений <T>, с именами контекста ее восстановления. При этом может оказаться (! только при отличии контекста восстановления <T> от контекста сереализации), что какая то часть восстановленных ссылочных значений не валидна (восстановленное значение отличается от сереализованного). Вероятность появления невалидности значений, скорее всего, будет очень малой, но не 0. Это надо учитывать и, для случая отличия контекста восстановления от контекста сереализации, желательно выполнять дополнительный контроль а, при необходимости и исправление.
----------
Модуль можно использовать свободно, а при распространении, желательна ссылка на меня. Сообщения об обнаруженных ошибках, замечания и вопросы будут мне интересны.
---
Одним из вариантов использования данного модуля может быть реализация контрольной точки, обеспечивающей продолжение выполнения любой цикличе-ской функции (аналога функции main в QLua) после ее перезапуска (по любой причине). При этом, для эффективности контрольной точки (уменьшения сохра-няемых данных) а также защищенности данных функции, можно реализовывать (по крайней мере, начиная с версии 5.3), с помощью _ENV, дополнительно к ее локальным переменным, собственное окружение функции, хранящее только ис-пользуемые в ней, ее сереализуемые значения. Такой вариант реализован мною в OS_Quesha для некоторых ее системных циклических функций.
Код модуля:
Замечу, что мои комментарии в ходе дискуссии с Артемом, особенно в части размеров кода, относились к функциональности, выложенного им модуля. Функциональность модифицированного модуля, а также эффективность выполнения в нем сереализации и восстановления таблиц, иные.
---
Код модуля выложен в конце данного комментария.
-----
В простом варианте восстановления сереализованной таблицы (в том числе и после перезапуска скрипта), когда контекст (определение приведено в описании модуля) ее восстановления не отличается от контекста сереализации, все просто:
-- Сереализация таблицы --
<Строка образа (! в случае отсутствия 3-го параметра)> = tbl_to_str (<Таблица> [, <Путь файла сохранения образа или вид результата (0 | 1 байт-код или строка-скрипт)> [ ,<Контекст сереализации>]])
………… !! Здесь может быть и перезапуск скрипта --
-- Полное восстановление таблицы (со всеми ее значениями без исключения ) --
<Таблица)> = str_to_tbl (<Сохраненный где образ - результат функции tbl_to_str > [,<Контекст сереализации>])
------
Более сложный вариант использования модуля (при различии контекста восстановления таблицы от контекста сереализации) приведен далее, в его описа-нии.
--------------------------------------------
В описании модуля значения типов function, userdata и thread называются ссылочными значениями.
Сереализуемая таблица, в описании, обозначена именем <T>.
Внутренним именем ссылочного значения называется результат функции internal_name (<Cсылочное значение>), определенной в модуле.
Составное имя имеет вид адреса поля в таблице: <T1>.<T2>…..<Tn>.<Поле таблицы Tn>
Под контекстом выполнения (сереализации, восстановления) далее описываемых функций понимается список доступных им, в момент их выполнения, ссылочных значений, на которые есть ссылки из <T>.
--
Описание модуля.
1. Существенных отличий данного модуля, от выложенного Артемом, три:
1) Формирование таблицы ссылочных значений <T> в формате, обеспечивающем, полное восстановление <T> (в том числе, с учетом ее локаль-ного окружения) в контексте ее сереализации из образа, сохраненного в файле (в том числе и после перезапуска скрипта). Кроме того, обеспечен удобный контроль и коррекция результата восстановления <T> в контексте, отличном от контекста ее сереализации.
2) Результатом сереализации таблицы <T> является Lua-скрипт восстановления этой таблицы (в виде байт-кода или исходного кода Lua), со-держащий в себе две таблицы ссылочных значений (контекста и <T>),
3) На таблице _G (в моем скрипте) время выполнения сереализации в дан-ном модуле в ~1,5 раза меньше, чем в сравниваемом модуле, а время восстановления _G меньше в ~ в восемьнадцать раз (мои конкретные числа данного модуля для _G ~ 8000 элементов: сереализация ~ 55 млсек., восстановление ~ 2 млсек.). С уменьшением сереализуемой таб-лицы, описанные выше времена, уменьшаются почти линейно.
2. В модуле доступны пять функций:
1) Сереализация таблицы:
tbl_to_str (<T> [, <Признак выдачи результата в виде байт-кода или строки-скрипта 0|1> | <Путь файла записи строки-скрипта > [ ,<Контекст сереализации: таблица или функция >] ])
Результат в зависимости от входных параметров: <байт-код восстановления <T> | <Строка-скрипт восстановления <T>> | <Запись строки-скрипта в файл с заданным путем>.
2) Восстановление таблицы:
str_to_tbl (< Байт-код скрипта для восстановления <T>, либо строка-скрипт>[ , <Контекст восстановления: таблица или функция >])
Результаты. Их может быть один или три (если есть висячие ссылочные значения):
1-й - восстановленная таблица с пометкой висячих ссылок, если такие существуют в виде составных имен в контексте сереализации.
Второй и третий – nil, если висячих ссылок нет или две таблицы:
- таблица висячих ссылок с указанием составных имен-адресов в кон-тексте сереализации;
- таблица висячих ссылок с указанием составных имен –адресов в <T>.
Эти данные могут быть основой для восстановление таких ссылок.
3) Формирование служебных таблиц, используемых в функциях tbl_to_str и str_to_tbl:
creating_link_tables (<Контекст выполнения функций tbl_to_str и str_to_tbl > [, <Признак обновления существующего контекста = 1>])
Первый параметр либо таблица (например, _G), либо пользователь-ская функция создания таблиц контекста (по образцу аналогично тому, как это делается в creating_link_tables). В случае функции, при создании | изменении контекста, вместо creating_link_tables запускается пользовательская функция. По умолчанию контекстом является _G.
Результаты функции две таблицы формируемые в ней.
4) Сбор (быстрый) строки из массива строк:
m_str_to_str_fast ( <Таблица-массив строк> [, <Признак последовательности сбора (false – прямой; true – обратный) >] ).
5) Печать любой таблицы Lua в виде удобном для анализа, с указанием уровней вложенности всех ее элементов:
dump_tbl (<Таблица>, <Символ отступов для выделения вложенности эле-ментов таблицы>, <Уровень раскрываемых вложенностей таблицы (если 0, то показывается все > [, <Вид выдачи результата: false – таблица (по умол-чанию); true - строка> [, < Таблица имен элементов (задаваемых в ключах) , не разворачиваемых далее >]]).
----
В квадратных скобках при описании всех функций указаны необязательные параметры.
3. При сереализации <T> ее ссылочные значения не сохраняются, но при восстановлении <T>, ссылки на ее ссылочные значения, разрешаются (устанавливают-ся на значения) в контексте ее восстановления. Если контекст, в момент восстановления <T>, такой же (в части всех ее ссылочных значений), как и в момент ее сереализации, то ничего кроме запуска функции str_to_tbl с единственным параметром-результатом, полученным из функции tbl_to_str не требуется; в этом случае таблица <T> будет восстановлена полностью (со всеми ее значениями, включая ссылочные).
При отличии контекста восстановления от контекста сереализации таблицы в момент ее восстановления, некоторые ссылки могут оказаться «висячими». Для возможности восстановления висячих ссылок функция str_to_tbl выдает дополнительно к восстановленной <T> c указанием в ней висячих ссылок, две таблицы описания таких ссылок в удобном для анализа виде.
4. В функции str_to_tbl выполняется автоматический контроль разрешимости ссылок <T> на ее ссылочные значения. Выполняемый контроль частичный, в том смысле, что если он не проходит, то это значит, что контекст восстановления <T> точно отличен от контекста ее сереализации. Если же этот контроль проходит, то это означает, что имеется всего лишь полное совпадение сформированных имен ссылочных значений <T>, с именами контекста ее восстановления. При этом может оказаться (! только при отличии контекста восстановления <T> от контекста сереализации), что какая то часть восстановленных ссылочных значений не валидна (восстановленное значение отличается от сереализованного). Вероятность появления невалидности значений, скорее всего, будет очень малой, но не 0. Это надо учитывать и, для случая отличия контекста восстановления от контекста сереализации, желательно выполнять дополнительный контроль а, при необходимости и исправление.
----------
Модуль можно использовать свободно, а при распространении, желательна ссылка на меня. Сообщения об обнаруженных ошибках, замечания и вопросы будут мне интересны.
---
Одним из вариантов использования данного модуля может быть реализация контрольной точки, обеспечивающей продолжение выполнения любой цикличе-ской функции (аналога функции main в QLua) после ее перезапуска (по любой причине). При этом, для эффективности контрольной точки (уменьшения сохра-няемых данных) а также защищенности данных функции, можно реализовывать (по крайней мере, начиная с версии 5.3), с помощью _ENV, дополнительно к ее локальным переменным, собственное окружение функции, хранящее только ис-пользуемые в ней, ее сереализуемые значения. Такой вариант реализован мною в OS_Quesha для некоторых ее системных циклических функций.
Код модуля:
Код |
---|
-- Модуль сериализации таблиц. Автор TGB --- --------------------------- --- Быстрая сборка строки из массива строк ---- -- Не обязательный параметр pr = false - строка собирается в прямом порядке (от начала массива к концу); true - строка собирается в обратном порядке -- local function m_str_to_str_fast ( m_str , pr) if type (m_str) ~= 'table' then return nil, ' ! Ошибка. Первый параметр m_str_to_str_fast не таблица ***' end local const_mod = 10, m_str_tm local mod, N , NN local N_mod, i_mod ---- if pr then m_str_tm = {} NN = #m_str for i = NN, 1, -1 do m_str_tm [NN - i +1] = m_str [i] end m_str = m_str_tm; m_str_tm = {} end while 1 do m_str_tm = {} mod, N, NN = #m_str % const_mod, math.floor (#m_str / const_mod), #m_str N_mod = N * const_mod for i = 1, N do i_mod = (i -1)*const_mod m_str_tm [i] = m_str [ i_mod + 1] .. m_str[ i_mod + 2] .. m_str [ i_mod + 3] .. m_str [ i_mod + 4] .. m_str [ i_mod + 5] .. m_str [ i_mod + 6] .. m_str [ i_mod + 7] .. m_str [ i_mod + 8] .. m_str [ i_mod + 9] .. m_str [ i_mod + const_mod] end if mod > 0 then N = N + 1; m_str_tm [N] = '' for i = 1, mod do m_str_tm [N] = m_str_tm [N] .. m_str [N_mod + i] end end ---- if #m_str_tm > 1 then m_str = m_str_tm else return m_str_tm [1] end end end ---------------------------------- ---------------------------------- --- Вывод произвольной таблицы в виде таблицы-массива строк (либо в виде строки) ------ ------ Параметры: -- 1) t - таблица (выводятся все вложения до limit); -- 2) i - строка формирования отступа при выводе вложений (например, ' ') ---- 3) limit - уровень вложенности до которого просматривается таблица (если = 0, то все уровни)----- ---- 4) (не обязательный) <Вид выдачи резкльтата: false – таблица (по умол-чанию); true - строка> ---- 5) (не обязательный) <Таблица имен элементов (задаваемых в ключах) , не разворачиваемых далее > --- ! Результат: таблица строк: структура со значениями таблицы t (текстовая сортировка по возрастанию индексов таблиц) либо строка, --- в зависимости от параметра pr----- --- !! Элемент таблицы [1] - заголовок таблицы. Остальные элементы - строковые представления структуры таблицы t --- с дополнительными служебными описаниями вложенных в t таблиц. local function dump_tbl (t, i, limit, pr, list) if type(t) ~= 'table' then if pr then return ' Первый параметр t не таблица. Результат: ' .. tostring(t) end return nil, '! Ошибка. Первый параметр t не таблица' end list = list or {} if type(list) ~= 'table' then return nil, '! Ошибка. Параметр list не таблица' end limit = limit or 0 local tbl = {}; --- для результата ---- tbl[#tbl +1] = '===== Таблица (текстовая сортировка по возрастанию индексов таблиц): ' .. tostring(t) .. ' \n ! Количество выводимых уровней вложенности (если = 0, то выводятся все) = ' .. limit .. '\n' local seen={} -- просмотренные -- local Level = 0 ------------------------------------------ local function dump(t,i, nm) ----- nm = nm or 'T' if seen [t] then return seen [t] end ------ seen [t] = nm --- ! local t_v, t_k, nm_tm Level = Level +1 --- Уровень вложенности таблицы ---- --- Обработка метатаблицы ---- local mt = debug.getmetatable ( t ) if mt then nm_tm = seen[mt] if nm_tm then tbl[#tbl +1] = i .. 'Для таблицы ' .. nm .. ' cсылка на существующую метатаблицу -> ' .. nm_tm .. '\n' else nm_tm = tostring(mt) tbl[#tbl +1] = i .. 'Вложенность: ' .. Level .. ' === Содержимое метатаблицы ### : ' .. nm_tm .. '\n' dump(mt, i..'\t', nm_tm) tbl[#tbl +1] = i .. 'Вложенность: ' .. Level .. ' === Конец метатаблицы ## ' .. nm_tm .. '\n' end end if next (t) == nil then ---- Таблица пустая --- tbl[#tbl +1] = i .. 'Вложенность: ' .. Level .. ' Таблица: ' .. nm .. ' пустая ### '.. '\n' Level = Level - 1 return '' end local s={} --- массив хранение имен ключей (строк) для сортировки --- local ss={} -- массив для хранения самих ключей -- local n=0 local ks=0 for k, v in next, t do ks = tostring(k) n=n+1 s[n] = ks ss[ks] = k end table.sort(s) for k,v in ipairs(s) do --- t_k = ss [v] -- ключ записи в t t_v=t [t_k] -- значение записи в t if Level < limit or limit == 0 then if type(t_k) == 'table' then --- Обработка ключа-таблицы --- nm_tm = seen[t_k] if nm_tm then tbl[#tbl +1] = i .. '[ ' .. tostring(t_k) .. ' - cсылка на существующую таблицу-ключ -> ' .. nm_tm .. ']' .. '\n' else tbl[#tbl +1] = i .. 'Вложенность: ' .. Level .. ' === Содержимое ключа-таблица $$ : ' .. v .. '\n' dump(t_k, i..'\t', tostring(t_k)) tbl[#tbl +1] = i .. 'Вложенность: ' .. Level .. ' === Конец ключа-таблицы $$ ' .. v .. '\n' end end if list [v] then if type(t_v) == 'table' then tbl[#tbl +1] = i .. '[' .. v .. '] (' .. type(t_v) .. ') = ' .. tostring(t_v) .. ' | Терминальная таблица (в печати задано далее не разворачивать) ### \n' else tbl[#tbl +1] = i .. '[' .. v .. '] (' .. type(t_v) .. ') = ' .. tostring(t_v) .. '\n' end else tbl[#tbl +1] = i .. '[' .. v .. '] (' .. type(t_v) .. ') = ' .. tostring(t_v) .. '\n' if type(t_v) == 'table' then --- Обработка таблицы ---- nm_tm = seen [ t_v] if nm_tm then tbl[#tbl +1] = i .. '[' .. v .. '] ссылка на существующую таблицу -> ' .. nm_tm .. '\n' else tbl[#tbl +1] = i .. 'Вложенность: ' .. Level .. ' === Содержимое таблицы : ' .. v .. '\n' dump(t_v, i..'\t', tostring(t_k)) tbl[#tbl +1] = i .. 'Вложенность: ' .. Level .. ' === Конец таблицы ' .. v .. '\n' end end end end end Level = Level - 1 return '' end -------------- dump(t,i, tostring(t)) if pr then --- Выдача результата в виде строки return m_str_to_str_fast ( tbl) else return tbl --- Выдача результата в виде массива строк end end -------------------------------- Общие данные модуля ----------------------------------------- --- 1. Для выделения строк используется скобки [[ ]] и поэтому внутри них не надо никаких преобразований (типа замены \ на \\ и т.д). -- 2. В выдаваемых стоках для удобства анализа, места, на которые надо обращать внимание в первую очередь выделены символом ###. ---- local aliases_cont = { } --- [ <Внутреннее имя ссылочного значения> ] = <Внешнее имя ссылочного значения в контексте> local aliases_v_cont = { } --- [ <Внешнее имя ссылочного значения в контексте> ] = <Ссылочное значение в контексте> local wo_creator -- Призак где сформирована таблицы local tbl_cont = _G --- Контекст по умолчанию --- local context_tbl = 'CONT' -- Имя, присвоенное контексту ----- ----- Формирование внутренних имен -- local function internal_name ( s ) return tostring ( s ):gsub ( ': ', '' ) end ---------------------------------------------- -- Параметры -- 1) 1-й параметр либо таблица, либо пользовательская функция соз-дания таблиц контекста (по образцу аналогично тому, -- как это делается в creating_link_tables). В случае функции, при создании, изменении контек-стэтом вместо creating_link_tables -- запускается пользовательская функ-ция. -- 2) необязательный, если 1, то принудительное обновление таблиц формируемых в ней -- Результаты : две таблицы формируемые в ней. --- local function creating_link_tables (context, pr) if type(context) == 'function' then wo_creator = 'no creating_link_tables' local cont, v_cont = context (pr) --- context должен выдавать два результата --- if cont then aliases_cont, aliases_v_cont = cont, v_cont end return aliases_cont, aliases_v_cont end ------ if pr then aliases_cont, aliases_v_cont = {}, {} end -- Обновление общих данных --- if next (aliases_cont) ~= nil and not pr then return end ---- if type(context) ~= 'table' then context = tbl_cont end ---- wo_creator = 'creating_link_tables' local path = '###: ' .. context_tbl ---------------------------- local function cr_link_tables (item, path, seen) seen = seen or { } path = path or context_tbl local mt if item == nil or seen [ item ] ~= nil then return elseif type ( item ) == 'table' then seen [ item ] = true -- Обработка метатаблицы (если есть в item)--- mt = debug.getmetatable ( item ) if type ( mt ) == 'table' then cr_link_tables (mt, path .. '.' .. '.metatable###', seen ) end --- local path_tm, kl_tm, k_tm for k, v in pairs ( item ) do if type ( v ) == 'table' then ---- таблица-значение -- cr_link_tables (v, path .. '.' .. tostring ( k ), seen ) else if not (type(v) == 'number' or type(v) == 'boolean' or type(v) == 'string') then if type(k) == 'string' then kl_tm = '.' .. tostring ( k ) else kl_tm = '[' .. tostring ( k ) .. '] ' end path_tm = path .. kl_tm k_tm = internal_name (v) if not aliases_cont [k_tm] then aliases_cont [k_tm] = path_tm end --- ### ?? возможна перезапись --- aliases_v_cont [ path_tm ] = v end end ---- if type ( k) == 'table' then ---- таблица-ключ -- cr_link_tables (k, path ..'.' .. tostring ( k ), seen ) else if not (type(k) == 'number' or type(k) == 'boolean' or type(k) == 'string') then if type(k) == 'string' then kl_tm = '.' .. tostring ( k ) else kl_tm = '[' .. tostring ( k ) .. '] ' end path_tm = '### ключ: ' .. path .. kl_tm k_tm = internal_name (k) if not aliases_cont [k_tm] then aliases_cont [k_tm] = path_tm end --- ### ?? возможна перезапись --- aliases_v_cont [ path_tm] = k end end end end end cr_link_tables (context, path) -- MessageDb ( 1, 1, 'Скрипт', ' Финальные проверки. creating_link_tables - aliases_v_cont : \n' .. dump_str ( aliases_v_cont , ' ', 0)) ---[[ -- Обработка внешних локальных ссылочных данных контекста --- local level = 2, ii, name, value local k_tm, path_tm while 1 do if debug.getinfo (level, 'n') then ii = 1 while 1 do name, value = debug.getlocal(level, ii) if name then if not ( type(value) == 'table' or type(value) == 'number' or type(value) == 'boolean' or type(value) == 'string') then k_tm = internal_name ( value) path_tm = '###: LOC.' .. tostring (level) .. '.' .. tostring (ii) .. '.' .. name if not aliases_cont [k_tm] then aliases_cont [k_tm] = path_tm end --- ### ?? возможна перезапись --- -- aliases_cont [k_tm] = path_tm aliases_v_cont [ path_tm ] = value end else break end ii = ii + 1 end else break end level = level + 1 end ---------------------------- ---]] return aliases_cont, aliases_v_cont end ------------------------------------------------------------ -- Функция создает байт-код либо строку-скрипт Lua, при запуске которого восстанавливается таблица t. -- В эту же строку в конец добавляется две таблицы ссылочных значений ---- Параметры: -- 1. t - сереализуемая таблица -- 2. ph_f - (не обязательный) путь файл сброса строки-скрипта либо признак (0 | 1) выдачи образа в виде (байт-кода | строки - скрипта) -- 3. context - - (не обязательный) контекст -- Результат в зависимости от входных параметров: <Функция Lua > (один тараметр) | <Строка-скрипт> (второй параметр = 1) -- | <Запись строки-скрипта в файл> (второй параметр строка путь к файлу) local function tbl_to_str ( t, ph_f, context) ----Контроль параметров --- if type ( t ) ~= 'table' then return nil, '!! Ошибка. Второй параметр не таблица.' end ph_f = ph_f or 0 local f_t if type(ph_f) == 'string' then f_t = io.open( ph_f, 'w') if type(f_t ) ~= 'userdata' then return nil, '!! Ошибка при открытии файла' end end creating_link_tables (context) ------ local name_alias = 'alias' --- для формирования команд с учетом alias ---- local env_t = context_tbl local env = env_t .. '.' local path = '###: TBL' local t_str = {} --- для сбора строк --- local name_t, context_t = {}, {} ----------------------------------------- local function tbl_str ( t, path, seen ) seen = seen or { } if seen [ t ] then return false, env .. internal_name ( t ) else seen [ t ] = 0 end ------- local n_t, mt = env .. internal_name ( t ), debug.getmetatable ( t ) t_str [ #t_str + 1 ] = n_t .. ' = {} --- создание таблицы: ' .. n_t .. ' -- \n' local n_tt, n_kt, n_vt , st, nm_t, path_t for k, v in pairs ( t ) do if (type(v) == 'number' or type(v) == 'boolean' or type(v) == 'string') then -- Простое значение -- if type(v) == 'string' then n_vt = '[[' .. v .. ']]' else n_vt = tostring (v) end elseif type ( v ) == 'table' then --- Таблица-значение -- n_vt = internal_name (k) t_str[ #t_str + 1 ] = ' --- ' .. n_vt .. ' - значение-таблица (начало) @ \n' st, n_vt = tbl_str ( v, path .. '.' .. n_vt, seen ) if not st then t_str [ #t_str + 1 ] = ' -- Таблица ' .. n_vt .. ' была создана ранее ----\n ' end t_str[ #t_str + 1 ] = ' --- ' .. n_vt .. ' - [' .. internal_name (v) .. '] значение-таблица (конец) @ \n' else --- Ссылочное значение --- nm_t = internal_name (v) path_t = path .. '.' .. internal_name (k) context_t [nm_t] = aliases_cont [nm_t] if name_t [nm_t] then name_t [nm_t] = name_t [nm_t] .. ' | ' .. path_t else name_t [nm_t] = path_t end n_vt = name_alias .. '[ [[' .. nm_t .. ']] ]' or '### err' end if (type(k) == 'number' or type(k) == 'boolean' or type(k) == 'string') then -- Простое значение -- if type(k) == 'string' then n_kt = '[[' .. k .. ']]' else n_kt = tostring (k) end elseif type ( k ) == 'table' then --- Таблица-ключ --- n_kt = internal_name (k) t_str[ #t_str + 1 ] = ' --- ' .. n_kt .. ' - ключ-таблица (начало) @ \n' st, n_kt = tbl_str ( k, path .. '.' .. n_kt, seen ) --- Создание таблицы ключа и ее 'скобок' ---- if not st then t_str[ #t_str + 1 ] = ' -- Таблица ' .. internal_name ( k ) .. ' была создана ранее ----\n ' end t_str[ #t_str + 1 ] = ' --- ' .. n_kt .. ' - [' .. internal_name (k) .. '] ключ таблица (конец) @ \n' --- else --- Ссылочное значение ключа --- nm_t = internal_name (k) path_t = path .. '.' .. nm_t context_t [nm_t] = aliases_cont [nm_t] name_t [nm_t] = path .. '.' .. nm_t .. '.ключ###' if name_t [nm_t] then name_t [nm_t] = name_t [nm_t] .. ' | ' .. path_t else name_t [nm_t] = path_t end n_kt = name_alias .. '[ [[' .. nm_t .. ']] ]' or '### err' end n_tt = env .. internal_name ( t ) t_str[ #t_str + 1 ] = n_tt .. ' [ ' .. n_kt .. ' ] = ' .. n_vt .. ' \n' end if mt then -- создание и подключение метатаблицы в конце прохода по таблице с тем, чтобы она не мешала заполнять таблицу. Иначе пришлось бы использовать rawset --- t_str[ #t_str + 1 ] = ' --- Создание метатаблицы --- \n' st, mt = tbl_str ( mt, path .. '.' .. internal_name (mt), seen ) if not st then t_str[ #t_str + 1 ] = '-- Метатаблица ' .. mt .. ' была создана ранее ----\n ' end t_str[ #t_str + 1 ] = 'debug.setmetatable ( ' .. n_t .. ', ' .. mt .. ' ) --- подключение метатаблицы ' .. mt .. ' --- \n' end return true, n_t --- 1 - й результат - признак создания таблицы end --------------------- tbl_str (t, path) --- Формирование фрагментов скрипта ----- local scr1 = 'local '.. name_alias .. ' = {...}; ' .. name_alias .. ' = ' .. name_alias .. ' [1] ; local ' .. env_t .. ' = {} \n' .. '\n if type(' .. name_alias .. ') == "table" then \n' local scr2 = '\n return ' .. t_str[1]: match ( '(%S*)') .. '\n else \n--- Две таблицы внутренних имен ссылочных значений : \n ' .. ' -- 1) name_cont [<Внутреннее имя ссылочного значения >] = ###: <Составное имя значения в контексте сереализации таблицы> \n' .. ' -- 2) name_t [<Внутреннее имя ссылочного значения >] = ###: <Список составных имен таблицы, ссылающиеся на значение > \n' .. '--- ! Составные имена таблицы (если их неколько) разделены символом | . \n' local scr3 = '\n local name_cont = { ' local scr4= '\n local name_t= { ' local scr5 = '\n} ' local scr6 = '\n return name_cont, name_t end \n ' ---------------------------- if type(f_t ) == 'userdata' then --- Запись строки-скрипта в файл ---- f_t: write (scr1) for i = 1, #t_str do f_t:write (t_str[i]) end f_t: write (scr2) f_t:write (scr3) for k, v in pairs ( context_t ) do f_t:write ( '\n [ [[' .. k .. ']] ] = [[' .. v ..']],' ) end f_t:write (scr5) f_t:write (scr4) for k, v in pairs ( name_t ) do f_t:write ( '\n [ [[' .. k .. ']] ] = [[' .. v ..']],' ) end f_t:write (scr5) f_t:write (scr6) f_t:close() return else ---------------------- local s_a = {} --- для сбора строк ---- ---- local s = scr1 .. m_str_to_str_fast ( t_str) s_a[ #s_a + 1 ] = scr2 s_a[ #s_a + 1 ] = scr3 for k, v in pairs ( context_t ) do s_a[ #s_a + 1 ] = '\n [ [[' .. k .. ']] ] = [[' .. v ..']],' end s_a[ #s_a + 1 ] = scr5 s_a[ #s_a + 1 ] = scr4 for k, v in pairs ( name_t ) do s_a[ #s_a + 1 ] = '\n [ [[' .. k .. ']] ] = [[' .. v ..']],' end s_a[ #s_a + 1 ] = scr5 s_a[ #s_a + 1 ] = scr6 s_a = m_str_to_str_fast ( s_a) -- массив строк -> строка ----- s = s .. s_a if ph_f == 1 then return s --- Строка-скрипт --- else return load ( s ) -- !!! байт-код скрипта (два результата) --- end end ------ end ---------------------------------------------------------------------- --- str_to_tbl (<Байт-код скрипта для восстановления <T>, либо строка-скрипт)>[, <Контекст>] ) --- Результаты. Их может быть один или три (если есть висячие ссылочные значения): -- 1-й - восстановленная таблица с gjvtnrjq висячих ссылок, если такие существуют в виде составных имен в контексте сереализации. -- Второй и третий – nil, если висячих ссылок нет или две таблицы: -- - таблица висячих ссылок с указанием составных имен-адресов в кон-тексте сереализации; -- - таблица висячих ссылок с указанием составных имен –адресов в <T>. -- В таблицах висячих ссылок есть данные, облегчающие восстановление этих ссылок.- ------ local function str_to_tbl (str_fun, context) --MessageDb ( 2, 0, 'Скрипт', ' str_fun str_fun: \n' .. dump_str ( str_fun , ' ', 0)) if type (str_fun) ~= 'string' and type (str_fun) ~= 'function' then return nil, '! Ошибка во 2-м параметре ф-ции str_to_tb (он должен быть типа string или function )' end local err if type (str_fun) == 'string' then str_fun , err = load ( str_fun ); if err ~= nil then return nil, err end end --------- local cont_t, tbl_t = str_fun () creating_link_tables ( context ) --- Проверка существования ссылочных значений таблицы ----- local cont_err, t_err = {}, {} local N_err = 0 local v_tm --- Первый этап разрешения ссылок --- for k, v in pairs ( cont_t ) do v_tm = aliases_v_cont [ v] if v_tm then --- есть ссылочное значение --- cont_t [k] = v_tm else N_err = N_err + 1 cont_err [ k] = v t_err [ k] = tbl_t [k] end end --- Второй этап разрешения ссылок --- if N_err > 0 then local cont_err_tm = cont_err for k, v in pairs ( cont_err_tm) do local i_mt = # v local v_tm, yes for i = i_mt , 1 do if v: sub(i, i) == ',' then i_mt = i; break end end if i_mt <= # v then for i = i_mt - 1 , 1 do if v: sub(i, i) == ',' then i_mt = i; break end end i_mt = i_mt + 1 v_tm = v: sub(i_mt) yes = nil for k_k, v_v in pairs ( aliases_v_cont ) do --- if k_k: find( v_tm, i_mt) then cont_t [k] = v_v; yes = 1 break end end if yes then N_err = N_err -1 cont_err [ k] = nil t_err [ k] = nil end end end end ----- if N_err > 0 then return str_fun (cont_t), cont_err, t_err else return str_fun (cont_t) end end ----- return { tbl_to_str = tbl_to_str, str_to_tbl = str_to_tbl, creating_link_tables = creating_link_tables, dump_tbl = dump_tbl , m_str_to_str_fast = m_str_to_str_fast } --- |