--[[ The OpenSSL stack library. Note `safestack` is not usable here in ffi because those symbols are eaten after preprocessing. Instead, we should do a Lua land type checking by having a nested field indicating which type of cdata its ctx holds. ]] local ffi = require "ffi" local C = ffi.C local ffi_cast = ffi.cast local ffi_gc = ffi.gc require "resty.openssl.include.stack" local format_error = require("resty.openssl.err").format_error local _M = {} local function gc_of(typ) local f = C[typ .. "_free"] return function (st) C.OPENSSL_sk_pop_free(st, f) end end _M.gc_of = gc_of _M.mt_of = function(typ, convert, index_tbl, no_gc) if type(typ) ~= "string" then error("expect a string at #1") elseif type(convert) ~= "function" then error("expect a function at #2") end local typ_ptr = typ .. "*" -- starts from 0 local function value_at(ctx, i) local elem = C.OPENSSL_sk_value(ctx, i) if elem == nil then error(format_error("OPENSSL_sk_value")) end local dup, err = convert(ffi_cast(typ_ptr, elem)) if err then error(err) end return dup end local function iter(tbl) if not tbl then error("instance is nil") end local i = 0 local n = tonumber(C.OPENSSL_sk_num(tbl.ctx)) return function() i = i + 1 if i <= n then return i, value_at(tbl.ctx, i-1) end end end local ret = { __pairs = iter, __ipairs = iter, __len = function(tbl) if not tbl then error("instance is nil") end return tonumber(C.OPENSSL_sk_num(tbl.ctx)) end, __index = function(tbl, k) if not tbl then error("instance is nil") end local i = tonumber(k) if not i then return index_tbl[k] end local n = C.OPENSSL_sk_num(tbl.ctx) if i <= 0 or i > n then return nil end return value_at(tbl.ctx, i-1) end, } if not no_gc then ret.__gc = gc_of(typ) end return ret end _M.new_of = function(typ) local gc = gc_of(typ) return function() local raw = C.OPENSSL_sk_new_null() if raw == nil then return nil, "stack.new_of: OPENSSL_sk_new_null() failed" end ffi_gc(raw, gc) return raw end end _M.add_of = function(typ) local ptr = ffi.typeof(typ .. "*") return function(stack, ctx) if not stack then error("instance is nil") end if ctx == nil or not ffi.istype(ptr, ctx) then return false, "stack.add_of: expect a " .. typ .. "* at #1" end local code = C.OPENSSL_sk_push(stack, ctx) if code == 0 then return false, "stack.add_of: OPENSSL_sk_push() failed" end return true end end local stack_ptr_ct = ffi.typeof("OPENSSL_STACK*") _M.dup_of = function(_) return function(ctx) if ctx == nil or not ffi.istype(stack_ptr_ct, ctx) then return nil, "stack.dup_of: expect a stack ctx at #1" end local ctx = C.OPENSSL_sk_dup(ctx) if ctx == nil then return nil, "stack.dup_of: OPENSSL_sk_dup() failed" end -- if the stack is duplicated: since we don't copy the elements -- then we only control gc of the stack itself here ffi_gc(ctx, C.OPENSSL_sk_free) return ctx end end -- fallback function to iterate if LUAJIT_ENABLE_LUA52COMPAT not enabled _M.all_func = function(mt) return function(stack) if not stack then error("stack is nil") end local ret = {} local _next = mt.__pairs(stack) while true do local i, elem = _next() if elem then ret[i] = elem else break end end return ret end end _M.deep_copy_of = function(typ) local dup = C[typ .. "_dup"] local free = C[typ .. "_free"] return function(ctx) return C.OPENSSL_sk_deep_copy(ctx, dup, free) end end return _M