Упаковка библиотеки C для Lua: как создать вложенные таблицы функций? - PullRequest
10 голосов
/ 02 марта 2012

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

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

API, к которому я стремлюсь, имеет вид:

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

или, более точно,

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

a_sha512_hash = hash.sha512('test')

Если вы ссылаетесь на polarssl.В приведенной выше ссылке вы увидите, что я написал функции, которые обертывают код 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'.

Когда я запускаю тестовый скрипт, похожий на код 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 ), но у всех, кажется, есть свой способ достижения этого.м полностью потерялся.

Ответы [ 2 ]

15 голосов
/ 02 марта 2012

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

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

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

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

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

lua_newtable(L);

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

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

lua_newtable(L);

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

lua_pushcfunction(L, hash_sha512);

Таким образом, в стеке есть три вещи: таблица назначения, таблица «hash» (мы будем «называть» ее через секунду) и функция, которую мы хотим поместить в таблицу «hash».

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

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

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

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

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

Теперь мы хотим поместить «хеш-таблицу» в таблицу, которую хотим вернуть. Это достаточно легко сделать с помощью другого 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

В таблице модуля теперь есть две записи: "hash" и "hmac". Обе таблицы с двумя функциями.

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

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

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

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

2 голосов
/ 02 марта 2012

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

В настоящее время я пытаюсь обернуть одну часть библиотеки PolarSSL (http://polarssl.org), чтобы дать мне доступ к HMAC SHA-512 (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.

...