Обтекание библиотеки C для Lua: как создать вложенные таблицы функций?

Код, связанный с этим вопросом, находится здесь: https://github.com/jchester/lua-polarssl/tree/master/src

В настоящее время я пытаюсь обернуть часть библиотеки PolarSSL (http://polarssl.org), чтобы предоставить мне доступ к SHA-512 HMAC (luacrypto не предоставляет этого).

API, на который я нацелен, имеет форму:

a_sha512_hash = polarssl.hash.sha512('text')

или более полно

local polarssl = require 'polarssl'
local hash = polarssl.hash

a_sha512_hash = hash.sha512('test')

Если вы ссылаетесь на polarssl.c в приведенной выше ссылке, вы увидите, что я написал функции, которые завершают код PolarSSL. Затем я пытаюсь построить таблицы функций таким образом:

LUA_API int luaopen_polarssl( lua_State *L ) {
  static const struct luaL_Reg core[] = {
    { NULL, NULL }
  };

  static const struct luaL_Reg hash_functions[] = {
    { "sha512", hash_sha512 },
    { "sha384", hash_sha384 },
    { NULL, NULL }
  };

  static const struct luaL_Reg hmac_functions[] = {
    { "sha512", hmac_sha512 },
    { "sha384", hmac_sha384 },
    { NULL, NULL }
  };

  luaL_register( L, CORE_MOD_NAME, core );
  luaL_register( L, HASH_MOD_NAME, hash_functions );
  luaL_register( L, HMAC_MOD_NAME, hmac_functions );

  return 1;
}

Где CORE_MOD_NAME = 'polarssl', HASH_MOD_NAME = 'polarssl.hash', HMAC_MOD_NAME = 'polarssl.hmac'.

Когда я запускаю тест script, похожий на код Lua в верхней части этого вопроса, я получаю следующее:

lua: test.lua:23: attempt to index global 'polarssl' (a nil value)
stack traceback:
    test.lua:23: in main chunk
    [C]: ?

Я попытался найти примеры того, как достичь этого подхода module.submodule(например, naim vs luasockets), но у всех, похоже, есть другой способ его достижения. Я полностью потерян.

Ответ 1

Кажется, что у каждого есть другой способ его достижения.

Это Луа; каждый делает это по-своему. Это лучшая сила и наибольшая слабость: язык предоставляет механизмы, а не политики.

Первое, что вам нужно сделать, это остановить использование luaL_register. Да, я знаю, это удобно. Но вы хотите что-то особенное, а luaL_register не поможет вам получить его.

Вы хотите создать таблицу, содержащую таблицу, содержащую одну или несколько функций. Итак... сделайте это.

Создайте таблицу.

lua_newtable(L);

Это было легко. Функция подталкивает таблицу к стеку, поэтому наш стек теперь имеет таблицу поверх нее. Это таблица, которую мы вернем.

Теперь нам нужно создать новую таблицу, чтобы войти в старую.

lua_newtable(L);

Опять же, легко. Затем мы хотим поместить функцию, которую хотим переместиться в эту таблицу в стеке.

lua_pushcfunction(L, hash_sha512);

Итак, в стеке есть три вещи: таблица назначения, таблица "хэш" (мы перейдем к "именованию" ее в секунду) и функции, которую мы хотим поместить в таблицу "хэш" .

Поэтому поместите функцию в хэш-таблицу.

lua_setfield(L, -2, "sha512");

Это занимает все, что находится поверх стека, и устанавливает его в поле с именем "sha512" в таблице по индексу -2 в стеке. Это где наш "хэш" стол. По завершении этой функции удаляет верхний элемент из стека. Это оставляет таблицу "хэш" наверху.

Мы можем повторить процесс для второй функции:

lua_pushcfunction(L, hash_sha384);
lua_setfield(L, -2, "sha384");

Теперь мы хотим поместить таблицу "hash" в таблицу, которую мы хотим вернуть. Это сделано достаточно легко с помощью другого вызова lua_setfield:

lua_setfield(L, -2, "hash");

Помните: эта функция выполняет все, что находится поверх стека. На этом этапе таблица, которую мы хотим вернуть (которая будет наша таблица модулей), находится в стеке.

Мы можем повторить этот процесс для таблицы "hmac":

lua_newtable(L); //Create "hmac" table
lua_pushcfunction(L, hmac_sha512);
lua_setfield(L, -2, "sha512");
lua_pushcfunction(L, hmac_sha384);  
lua_setfield(L, -2, "sha384");
lua_setfield(L, -2, "hmac"); //Put the "hmac" table into our module table

В таблице модулей теперь есть две записи: "хэш" и "hmac". Оба являются таблицами с двумя функциями в них.

Мы можем привязать его к глобальной таблице следующим образом:

lua_pushvalue(L, -1);
lua_setfield(L, LUA_GLOBALSINDEX, "polarssl");

Не каждый производитель модулей хочет это сделать. Некоторые предпочитают принуждать людей использовать синтаксис local polarssl = require "polarssl", чтобы избежать загрязнения глобального пространства имен. Это зависит от вас.

Но то, что вы должны сделать в любом случае, - это вернуть эту таблицу. Верните 1 из вашей функции luaopen, чтобы Lua знал, что есть одно возвращаемое значение. Вышеуказанный вызов lua_pushvalue существует с единственной целью копирования таблицы (помните: на таблицы ссылаются, поэтому, как и копирование указателя). Таким образом, когда вы используете lua_setfield, копия удаляется из стека, в то время как оригинал остается использоваться как возвращаемое значение.

Ответ 2

Не имеет прямого отношения к вопросу, но следующее утверждение не совсем верно:

В настоящее время я пытаюсь обернуть часть библиотеки PolarSSL (http://polarssl.org), чтобы предоставить мне доступ к SHA-512 HMAC (luacrypto не предоставляет этого).

Я не знаю, какую версию LuaCrypto вы имеете в виду, но эта вилка LuaCrypto предоставляет SHA-512 HMAC, а также любые другие тип дайджеста, поддерживаемый OpenSSL автоматически. Просто введите "sha512" в качестве типа дайджеста:

hmac.digest("sha512", message, key)

В документации указывается только часть поддерживаемых типов дайджеста, полный список можно получить, вызвав crypto.list("digests").

table.foreach(crypto.list("digests"), print)

Когда я думаю об этом, даже оригинальный LuaCrypto должен поддерживать SHA-512.