[openresty-en]

88 views
Skip to first unread message

Robert Paprocki

unread,
Nov 2, 2014, 8:54:10 PM11/2/14
to openre...@googlegroups.com
Hello!

I am attempting to use FFI to leverage the ngx_hash_key function in
src/core/ngx_hash.c; however, because it returns a uintptr_t, I'm having
trouble getting it into other portions of my Lua code. For example, a
compiled-in implementation of the Jenkins one-at-a-time hash that
returns a unint32_t works just fine with the following code:

ffi.cdef[[
uint32_t JHASH(char *key, size_t len);
]]

local function jhash(str)
local buf = ffi.cast("char *", str)
local c = ffi.C.JHASH(buf, string.len(str))
ngx.say(c)
end

Where 'JHASH' is defined to:

uint32_t jenkins_one_at_a_time_hash(char *key, size_t len)
{
unsigned char *p = key;
uint32_t h = 0;
int i;

for ( i = 0; i < len; i++ )
h ^= ( h << 5 ) + ( h >> 2 ) + p[i];

return h;
}


This code works no problem. However, when trying to use ngx_hash_key as
follows:

ffi.cdef[[
typedef uintptr_t ngx_uint_t;
ngx_uint_t ngx_hash_key(char *key, size_t len);
]]

local function hash(str)
local buf = ffi.cast("char *", str)
local c = ffi.C.ngx_hash_key(buf, string.len(str))
ngx.say(c)
end

I see the following error:

bad argument #1 to 'say' (string, number, boolean, nil, ngx.null, or
array table expected, but got cdata).

Based on http://luajit.org/ext_ffi_semantics.html, I think this is
because ngx_hash_key is getting converted to '64 bit int cdata', not a
number. Any input on how to get this value to a type that Lua can
properly handle? Thanks!

Yichun Zhang (agentzh)

unread,
Nov 3, 2014, 3:11:40 PM11/3/14
to openresty-en
Hello!

On Sun, Nov 2, 2014 at 5:54 PM, Robert Paprocki wrote:
> I am attempting to use FFI to leverage the ngx_hash_key function in
> src/core/ngx_hash.c; however, because it returns a uintptr_t, I'm having
> trouble getting it into other portions of my Lua code.

One caveat: it is considered bad practice to call into nginx C API via
FFI because nginx does not have an ABI so the C API may change across
nginx releases and your app can have mysterious and hard-to-debug
issues since then.

>
> This code works no problem. However, when trying to use ngx_hash_key as
> follows:
>
> ffi.cdef[[
> typedef uintptr_t ngx_uint_t;
> ngx_uint_t ngx_hash_key(char *key, size_t len);

You should use "const char *key" instead of "char *key" here.

> ]]
>
> local function hash(str)
> local buf = ffi.cast("char *", str)

This way you can save this ffi.cast() call.

> local c = ffi.C.ngx_hash_key(buf, string.len(str))
> ngx.say(c)

ngx.say() does not accept cdata typed input and you need to stringify
it before feeding it into ngx.say().

> I see the following error:
>
> bad argument #1 to 'say' (string, number, boolean, nil, ngx.null, or
> array table expected, but got cdata).
>
> Based on http://luajit.org/ext_ffi_semantics.html, I think this is
> because ngx_hash_key is getting converted to '64 bit int cdata', not a
> number.

Right.

> Any input on how to get this value to a type that Lua can
> properly handle? Thanks!
>

If you know a double can hold your integer value losslessly, then you
can use tonumber() directly. Otherwise you need to use LuaJIT v2.1's
builtin "bit" module [1] to stringify the higher 32 bit part and the
lower 32 bit part separately and concatenate the two parts into a
single Lua string.

Regards,
-agentzh

[1] http://luajit.org/extensions.html
Reply all
Reply to author
Forward
0 new messages