-- 打印表格
function PrintTable(t, indent, done)
    --print ( string.format ('PrintTable type %s', type(keys)) )
    if type(t) ~= "table" then return end

    done = done or {}
    done[t] = true
    indent = indent or 0

    local l = {}
    for k, v in pairs(t) do
        table.insert(l, k)
    end

    table.sort(l)
    for k, v in ipairs(l) do
        -- Ignore FDesc
        if v ~= 'FDesc' then
            local value = t[v]

            if type(value) == "table" and not done[value] then
                done [value] = true
                print(string.rep ("\t", indent)..tostring(v)..":")
                PrintTable (value, indent + 2, done)
            elseif type(value) == "userdata" and not done[value] then
                done [value] = true
                print(string.rep ("\t", indent)..tostring(v)..": "..tostring(value))
                PrintTable ((getmetatable(value) and getmetatable(value).__index) or getmetatable(value), indent + 2, done)
            else
                if t.FDesc and t.FDesc[v] then
                    print(string.rep ("\t", indent)..tostring(t.FDesc[v]))
                else
                    print(string.rep ("\t", indent)..tostring(v)..": "..tostring(value))
                end
            end
        end
    end
end



-- ***** string 功能扩展开始 ******

    -- RC4
    -- http://en.wikipedia.org/wiki/RC4
    function RC4encrypt(text,key) 

        function KSA(key)
            local key_len = string.len(key)
            local S = {}
            local key_byte = {}

            for i = 0, 255 do
                S[i] = i
            end

            for i = 1, key_len do
                key_byte[i-1] = string.byte(key, i, i)
            end

            local j = 0
            for i = 0, 255 do
                j = (j + S[i] + key_byte[i % key_len]) % 256
                S[i], S[j] = S[j], S[i]
            end
            return S
        end

        function PRGA(S, text_len)
            local i = 0
            local j = 0
            local K = {}

            for n = 1, text_len do

                i = (i + 1) % 256
                j = (j + S[i]) % 256

                S[i], S[j] = S[j], S[i]
                K[n] = S[(S[i] + S[j]) % 256]
            end
            return K
        end

        function RC4(key, text)
            local text_len = string.len(text)

            local S = KSA(key)        
            local K = PRGA(S, text_len) 
            return output(K, text)
        end

        function output(S, text)
            local len = string.len(text)
            local c = nil
            local res = {}
            for i = 1, len do
                c = string.byte(text, i, i)
                res[i] = string.char(bxor(S[i], c))
            end
            return table.concat(res)
        end


        -------------------------------
        -------------bit wise-----------
        -------------------------------

        local bit_op = {}
        function bit_op.cond_and(r_a, r_b)
            return (r_a + r_b == 2) and 1 or 0
        end

        function bit_op.cond_xor(r_a, r_b)
            return (r_a + r_b == 1) and 1 or 0
        end

        function bit_op.cond_or(r_a, r_b)
            return (r_a + r_b > 0) and 1 or 0
        end

        function bit_op.base(op_cond, a, b)
            -- bit operation
            if a < b then
                a, b = b, a
            end
            local res = 0
            local shift = 1
            while a ~= 0 do
                r_a = a % 2
                r_b = b % 2
        
                res = shift * bit_op[op_cond](r_a, r_b) + res 
                shift = shift * 2

                a = math.modf(a / 2)
                b = math.modf(b / 2)
            end
            return res
        end

        function bxor(a, b)
            return bit_op.base('cond_xor', a, b)
        end

        function band(a, b)
            return bit_op.base('cond_and', a, b)
        end

        function bor(a, b)
            return bit_op.base('cond_or', a, b)
        end

        local textLen = string.len(text)
        local schedule = KSA(key)
        local k = PRGA(schedule, textLen)
        return output(k, text)
    end


    --将字符串按格式转为16进制串
    function string.tohex(str)
        --判断输入类型
        if (type(str)~="string") then
            return nil,"string2hex invalid input type"
        end
        --拼接字符串
        local index=1
        local ret=""
        for index=1,str:len() do
            ret=ret..string.format("%02X",str:sub(index):byte())
        end
    
        return ret
    end

    --将16进制串转换为字符串
    function string.fromhex(hex)
        --判断输入类型	
        if (type(hex)~="string") then
            return nil,"hex2string invalid input type"
        end
        --滤掉分隔符
        hex=hex:gsub("[%s%p]",""):upper()
        --检查内容是否合法
        if(hex:find("[^0-9A-Fa-f]")~=nil) then
            return nil,"hex2string invalid input content"
        end
        --检查字符串长度
        if(hex:len()%2~=0) then
            return nil,"hex2string invalid input lenth"
        end
        --拼接字符串
        local index=1
        local ret=""
        for index=1,hex:len(),2 do
            ret=ret..string.char(tonumber(hex:sub(index,index+1),16))
        end
    
        return ret
    end


    -- 字符串编码
    function string.encode(text, key) 
        if text == nil or string.len(text) == 0 then return text end 
        return string.tohex(RC4encrypt(text, key))     
    end

    -- 字符串解码
    function string.decode(text, key) 
        if text == nil or string.len(text) == 0 then return text end 
        local s,e = string.fromhex(text)
        if s == nil then 
            return s,e 
        else
            return RC4encrypt(s, key)
        end   
    end

    -- 字符串分割，返回分割后子串数组
    function string.split(input, delimiter)
        input = tostring(input)
        delimiter = tostring(delimiter)
        if (delimiter=='') then return false end
        local pos,arr = 0, {}
        for st,sp in function() return string.find(input, delimiter, pos, true) end do
            table.insert(arr, string.sub(input, pos, st - 1))
            pos = sp + 1
        end
        table.insert(arr, string.sub(input, pos))
        return arr
    end

    -- 字符串输出到文件
    function string.tofile(str, path)
        local out = io.open(path,"w")
        if out then
            out:write(str)
            out:close()
        end
    end

    -- 超长字符串格式化的方式输出到文件，使用方法 table.concat(t)
    function string.tablefile(str, path)
        local out = io.open(path,"w")
        if out then
            out:write('{\n')
            local len = string.len(str)
            local step = 255
            local n = math.floor(len/step)            
            for i=0,n-1 do
                out:write('    "'..string.sub(str, i*step+1, (i+1)*step)..'",\n')
            end
            out:write('    "'..string.sub(str, n*step+1, (n+1)*step)..'"\n}')       
            out:close()
        end
    end

    -- 从kv文件中载入字符串，对应 string.savekvfile 的输出
    function string.loadkvfile(path)
        local t = LoadKeyValues(path)
		if t == nil or t["1"] == nil then return nil end	

        local ret = t["1"]
        local i = 2
        while(t[tostring(i)] ~= nil)
        do
            ret = ret..t[tostring(i)]
            i = i + 1
        end
        t = {}
        return ret		
    end
    
    --[[ 
        以字符串内容写入文件，成功返回 true，失败返回 false
        "mode 写入模式" 参数决定 io.writefile() 如何写入内容，可用的值如下：
        -   "w+" : 覆盖文件已有内容，如果文件不存在则创建新文件
        -   "a+" : 追加内容到文件尾部，如果文件不存在则创建文件    
        此外，还可以在 "写入模式" 参数最后追加字符 "b" ，表示以二进制方式写入数据，这样可以避免内容写入不完整。
        - 默认值为 "w+b"
        path 文件完全路径
        content 要写入的内容
    --]] 
    function string.writefile(content, path, mode)
        mode = mode or "w+b"
        local file = io.open(path, mode)
        if file then
            if file:write(content) == nil then return false end
            io.close(file)
            return true
        else
            return false
        end
    end

-- ***** string 功能扩展结束 ******



-- ***** table 功能扩展开始 ******

    -- table和string的互转
    function table.tostring(t)

        function ToStringEx(value)
            if type(value)=='table' then
            return table.tostring(value)
            elseif type(value)=='string' then
                return "\'"..value.."\'"
            else
            return tostring(value)
            end
        end

        if t == nil then return "" end
        local retstr= "{"

        local i = 1
        for key,value in pairs(t) do
            local signal = ","
            if i==1 then
            signal = ""
            end

            if key == i then
                retstr = retstr..signal..ToStringEx(value)
            else
                if type(key)=='number' or type(key) == 'string' then
                    retstr = retstr..signal..'['..ToStringEx(key).."]="..ToStringEx(value)
                else
                    if type(key)=='userdata' then
                        retstr = retstr..signal.."*s"..table.tostring(getmetatable(key)).."*e".."="..ToStringEx(value)
                    else
                        retstr = retstr..signal..key.."="..ToStringEx(value)
                    end
                end
            end

            i = i+1
        end

        retstr = retstr.."}"
        return retstr
    end

    -- table和string的互转
    function table.fromstring(str)
        if str == nil or type(str) ~= "string" then
            return
        end
        -- loadstring在lua5.2中已经被弃用了
        -- return loadstring("return " .. str)()   
        return load("return " .. str)()   
    end

    -- table按键名排序遍历，默认为升序
    -- t为table, method为排序方式，asc或desc，func为遍历回调
    -- lua中table是按照hash排列的，ipairs可以顺序遍历，但是不一定全部结果都能遍历出来，pairs能够全部遍历，但是遍历出来的结果是随机的，故需使用此方法
    function table.sorteach(t, func, method) 
        local tableTemp = {}
        for i in pairs(t) do
            if i ~= nil then 
                table.insert(tableTemp, i)  
            end
        end
        if method == "desc" or method == "DESC" then 
            -- 降序
            table.sort(tableTemp, function(a,b) return (a > b) end)
        else        
            -- 升序
            table.sort(tableTemp, function(a,b) return (a < b) end)  
        end
        
        -- 遍历
        for _, key in pairs(tableTemp) do  
            if type(func) == "function" then func(key, t[key]) end
        end   
        tableTemp = {}
    end

    -- 深度复制表
    function table.deepcopy(object)
        local lookup_table = {}
        local function _copy(object)
            if type(object) ~= "table" then
                return object
            elseif lookup_table[object] then
                return lookup_table[object]
            end
            local new_table = {}
            lookup_table[object] = new_table
            for index, value in pairs(object) do
                new_table[_copy(index)] = _copy(value)
            end
            return setmetatable(new_table, getmetatable(object))
        end
        return _copy(object)
    end

    -- 判断键是否在table中
    function table.haskey(t, key)
        for k,v in pairs(t) do
            if k == key then return true end
        end
        return false
    end

    -- 判断值是否在table中
    function table.hasvalue(t, value)
        for k,v in pairs(t) do
            if v == value then return true end
        end
        return false
    end

    -- 从文件中载入数据，来源对应加密数据
    function table.loadkv(path) 
        -- return table.fromstring(string.decode(string.loadkvfile(path), ""))
        return table.fromstring(string.decode(string.loadkvfile(path), SERVER_KEY))
    end

    -- 获取表中值元素的长度，不计nil 
    function table.count(t) 
        if type(t) ~= "table" then
            return 0
        end
        local len = 0
        for k,v in pairs(t) do
            if v ~= nil then 
                len = len + 1 
            end
        end
        return len
    end

    -- 获取表按升序排列的第index元素对应的key（排除值为nil的元素），异常返回 nil
    function table.getkey(t, index) 
        if type(t) ~= "table" or index == nil then
            return nil
        end
    
        local tableTemp = {}
        for k in pairs(t) do
            if k ~= nil then 
                table.insert(tableTemp, k)  
            end
        end  
        -- 降序
        -- table.sort(tableTemp, function(a,b) return (a > b) end)    
        -- 升序
        table.sort(tableTemp, function(a,b) return (a < b) end)
        
        local key = nil
        local count = 0
        if index >= 1 or index <= #tableTemp then 
            key = tableTemp[index]
        end
        tableTemp = {}
        return key
    end

    -- 按 key 顺序循环，f为可选参数，用于指定排序顺序
    function pairsByKeys(t,f) 
        local a = {}
        for n in pairs(t) do a[#a + 1] = n end
        table.sort(a, f)
        local i = 0
        return function()
            i = i + 1
            return a[i], t[a[i]]
        end
    end
    --~ 使用上述迭代器：
    -- for k,v in pairsByKeys(inputTable) do
    --     print(k, v)
    -- end

    -- 按 key 顺序获取指定key的序号
    function table.keyindex(t, key)         
        local index = 0
        for k,v in pairsByKeys(t) do
            index = index + 1
            if k == key then 
                return index
            end
        end
        return nil
    end

-- ***** table 功能扩展结束 ******



-- ***** MD5 功能开始 ******

    -- 使用 md5.sumhexa(message)
    md5 = {
    _VERSION     = "md5.lua 1.1.0",
    _DESCRIPTION = "MD5 computation in Lua (5.1-3, LuaJIT)",
    _URL         = "https://github.com/kikito/md5.lua",
    _LICENSE     = [[
        MIT LICENSE

        Copyright (c) 2013 Enrique García Cota + Adam Baldwin + hanzao + Equi 4 Software

        Permission is hereby granted, free of charge, to any person obtaining a
        copy of this software and associated documentation files (the
        "Software"), to deal in the Software without restriction, including
        without limitation the rights to use, copy, modify, merge, publish,
        distribute, sublicense, and/or sell copies of the Software, and to
        permit persons to whom the Software is furnished to do so, subject to
        the following conditions:

        The above copyright notice and this permission notice shall be included
        in all copies or substantial portions of the Software.

        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
        OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
        MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
        IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
        CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
        TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
        SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    ]]
    }

    -- bit lib implementions

    local char, byte, format, rep, sub =
    string.char, string.byte, string.format, string.rep, string.sub
    local bit_or, bit_and, bit_not, bit_xor, bit_rshift, bit_lshift

    local ok, bit = pcall(require, 'bit')
    if ok then
    bit_or, bit_and, bit_not, bit_xor, bit_rshift, bit_lshift = bit.bor, bit.band, bit.bnot, bit.bxor, bit.rshift, bit.lshift
    else
    ok, bit = pcall(require, 'bit32')

    if ok then

        bit_not = bit.bnot

        local tobit = function(n)
        return n <= 0x7fffffff and n or -(bit_not(n) + 1)
        end

        local normalize = function(f)
        return function(a,b) return tobit(f(tobit(a), tobit(b))) end
        end

        bit_or, bit_and, bit_xor = normalize(bit.bor), normalize(bit.band), normalize(bit.bxor)
        bit_rshift, bit_lshift = normalize(bit.rshift), normalize(bit.lshift)

    else

        local function tbl2number(tbl)
        local result = 0
        local power = 1
        for i = 1, #tbl do
            result = result + tbl[i] * power
            power = power * 2
        end
        return result
        end

        local function expand(t1, t2)
        local big, small = t1, t2
        if(#big < #small) then
            big, small = small, big
        end
        -- expand small
        for i = #small + 1, #big do
            small[i] = 0
        end
        end

        local to_bits -- needs to be declared before bit_not

        bit_not = function(n)
        local tbl = to_bits(n)
        local size = math.max(#tbl, 32)
        for i = 1, size do
            if(tbl[i] == 1) then
            tbl[i] = 0
            else
            tbl[i] = 1
            end
        end
        return tbl2number(tbl)
        end

        -- defined as local above
        to_bits = function (n)
        if(n < 0) then
            -- negative
            return to_bits(bit_not(math.abs(n)) + 1)
        end
        -- to bits table
        local tbl = {}
        local cnt = 1
        local last
        while n > 0 do
            last      = n % 2
            tbl[cnt]  = last
            n         = (n-last)/2
            cnt       = cnt + 1
        end

        return tbl
        end

        bit_or = function(m, n)
        local tbl_m = to_bits(m)
        local tbl_n = to_bits(n)
        expand(tbl_m, tbl_n)

        local tbl = {}
        for i = 1, #tbl_m do
            if(tbl_m[i]== 0 and tbl_n[i] == 0) then
            tbl[i] = 0
            else
            tbl[i] = 1
            end
        end

        return tbl2number(tbl)
        end

        bit_and = function(m, n)
        local tbl_m = to_bits(m)
        local tbl_n = to_bits(n)
        expand(tbl_m, tbl_n)

        local tbl = {}
        for i = 1, #tbl_m do
            if(tbl_m[i]== 0 or tbl_n[i] == 0) then
            tbl[i] = 0
            else
            tbl[i] = 1
            end
        end

        return tbl2number(tbl)
        end

        bit_xor = function(m, n)
        local tbl_m = to_bits(m)
        local tbl_n = to_bits(n)
        expand(tbl_m, tbl_n)

        local tbl = {}
        for i = 1, #tbl_m do
            if(tbl_m[i] ~= tbl_n[i]) then
            tbl[i] = 1
            else
            tbl[i] = 0
            end
        end

        return tbl2number(tbl)
        end

        bit_rshift = function(n, bits)
        local high_bit = 0
        if(n < 0) then
            -- negative
            n = bit_not(math.abs(n)) + 1
            high_bit = 0x80000000
        end

        local floor = math.floor

        for i=1, bits do
            n = n/2
            n = bit_or(floor(n), high_bit)
        end
        return floor(n)
        end

        bit_lshift = function(n, bits)
        if(n < 0) then
            -- negative
            n = bit_not(math.abs(n)) + 1
        end

        for i=1, bits do
            n = n*2
        end
        return bit_and(n, 0xFFFFFFFF)
        end
    end
    end

    -- convert little-endian 32-bit int to a 4-char string
    local function lei2str(i)
    local f=function (s) return char( bit_and( bit_rshift(i, s), 255)) end
    return f(0)..f(8)..f(16)..f(24)
    end

    -- convert raw string to big-endian int
    local function str2bei(s)
    local v=0
    for i=1, #s do
        v = v * 256 + byte(s, i)
    end
    return v
    end

    -- convert raw string to little-endian int
    local function str2lei(s)
    local v=0
    for i = #s,1,-1 do
        v = v*256 + byte(s, i)
    end
    return v
    end

    -- cut up a string in little-endian ints of given size
    local function cut_le_str(s,...)
    local o, r = 1, {}
    local args = {...}
    for i=1, #args do
        table.insert(r, str2lei(sub(s, o, o + args[i] - 1)))
        o = o + args[i]
    end
    return r
    end

    local swap = function (w) return str2bei(lei2str(w)) end

    -- An MD5 mplementation in Lua, requires bitlib (hacked to use LuaBit from above, ugh)
    -- 10/02/2001 jcw@equi4.com

    local CONSTS = {
    0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
    0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
    0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
    0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
    0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
    0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
    0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
    0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
    0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
    0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
    0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
    0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
    0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
    0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
    0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
    0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391,
    0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476
    }

    local f=function (x,y,z) return bit_or(bit_and(x,y),bit_and(-x-1,z)) end
    local g=function (x,y,z) return bit_or(bit_and(x,z),bit_and(y,-z-1)) end
    local h=function (x,y,z) return bit_xor(x,bit_xor(y,z)) end
    local i=function (x,y,z) return bit_xor(y,bit_or(x,-z-1)) end
    local z=function (ff,a,b,c,d,x,s,ac)
    a=bit_and(a+ff(b,c,d)+x+ac,0xFFFFFFFF)
    -- be *very* careful that left shift does not cause rounding!
    return bit_or(bit_lshift(bit_and(a,bit_rshift(0xFFFFFFFF,s)),s),bit_rshift(a,32-s))+b
    end

    local function transform(A,B,C,D,X)
    local a,b,c,d=A,B,C,D
    local t=CONSTS

    a=z(f,a,b,c,d,X[ 0], 7,t[ 1])
    d=z(f,d,a,b,c,X[ 1],12,t[ 2])
    c=z(f,c,d,a,b,X[ 2],17,t[ 3])
    b=z(f,b,c,d,a,X[ 3],22,t[ 4])
    a=z(f,a,b,c,d,X[ 4], 7,t[ 5])
    d=z(f,d,a,b,c,X[ 5],12,t[ 6])
    c=z(f,c,d,a,b,X[ 6],17,t[ 7])
    b=z(f,b,c,d,a,X[ 7],22,t[ 8])
    a=z(f,a,b,c,d,X[ 8], 7,t[ 9])
    d=z(f,d,a,b,c,X[ 9],12,t[10])
    c=z(f,c,d,a,b,X[10],17,t[11])
    b=z(f,b,c,d,a,X[11],22,t[12])
    a=z(f,a,b,c,d,X[12], 7,t[13])
    d=z(f,d,a,b,c,X[13],12,t[14])
    c=z(f,c,d,a,b,X[14],17,t[15])
    b=z(f,b,c,d,a,X[15],22,t[16])

    a=z(g,a,b,c,d,X[ 1], 5,t[17])
    d=z(g,d,a,b,c,X[ 6], 9,t[18])
    c=z(g,c,d,a,b,X[11],14,t[19])
    b=z(g,b,c,d,a,X[ 0],20,t[20])
    a=z(g,a,b,c,d,X[ 5], 5,t[21])
    d=z(g,d,a,b,c,X[10], 9,t[22])
    c=z(g,c,d,a,b,X[15],14,t[23])
    b=z(g,b,c,d,a,X[ 4],20,t[24])
    a=z(g,a,b,c,d,X[ 9], 5,t[25])
    d=z(g,d,a,b,c,X[14], 9,t[26])
    c=z(g,c,d,a,b,X[ 3],14,t[27])
    b=z(g,b,c,d,a,X[ 8],20,t[28])
    a=z(g,a,b,c,d,X[13], 5,t[29])
    d=z(g,d,a,b,c,X[ 2], 9,t[30])
    c=z(g,c,d,a,b,X[ 7],14,t[31])
    b=z(g,b,c,d,a,X[12],20,t[32])

    a=z(h,a,b,c,d,X[ 5], 4,t[33])
    d=z(h,d,a,b,c,X[ 8],11,t[34])
    c=z(h,c,d,a,b,X[11],16,t[35])
    b=z(h,b,c,d,a,X[14],23,t[36])
    a=z(h,a,b,c,d,X[ 1], 4,t[37])
    d=z(h,d,a,b,c,X[ 4],11,t[38])
    c=z(h,c,d,a,b,X[ 7],16,t[39])
    b=z(h,b,c,d,a,X[10],23,t[40])
    a=z(h,a,b,c,d,X[13], 4,t[41])
    d=z(h,d,a,b,c,X[ 0],11,t[42])
    c=z(h,c,d,a,b,X[ 3],16,t[43])
    b=z(h,b,c,d,a,X[ 6],23,t[44])
    a=z(h,a,b,c,d,X[ 9], 4,t[45])
    d=z(h,d,a,b,c,X[12],11,t[46])
    c=z(h,c,d,a,b,X[15],16,t[47])
    b=z(h,b,c,d,a,X[ 2],23,t[48])

    a=z(i,a,b,c,d,X[ 0], 6,t[49])
    d=z(i,d,a,b,c,X[ 7],10,t[50])
    c=z(i,c,d,a,b,X[14],15,t[51])
    b=z(i,b,c,d,a,X[ 5],21,t[52])
    a=z(i,a,b,c,d,X[12], 6,t[53])
    d=z(i,d,a,b,c,X[ 3],10,t[54])
    c=z(i,c,d,a,b,X[10],15,t[55])
    b=z(i,b,c,d,a,X[ 1],21,t[56])
    a=z(i,a,b,c,d,X[ 8], 6,t[57])
    d=z(i,d,a,b,c,X[15],10,t[58])
    c=z(i,c,d,a,b,X[ 6],15,t[59])
    b=z(i,b,c,d,a,X[13],21,t[60])
    a=z(i,a,b,c,d,X[ 4], 6,t[61])
    d=z(i,d,a,b,c,X[11],10,t[62])
    c=z(i,c,d,a,b,X[ 2],15,t[63])
    b=z(i,b,c,d,a,X[ 9],21,t[64])

    return bit_and(A+a,0xFFFFFFFF),bit_and(B+b,0xFFFFFFFF),
            bit_and(C+c,0xFFFFFFFF),bit_and(D+d,0xFFFFFFFF)
    end

    ----------------------------------------------------------------

    local function md5_update(self, s)
    self.pos = self.pos + #s
    s = self.buf .. s
    for ii = 1, #s - 63, 64 do
        local X = cut_le_str(sub(s,ii,ii+63),4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4)
        assert(#X == 16)
        X[0] = table.remove(X,1) -- zero based!
        self.a,self.b,self.c,self.d = transform(self.a,self.b,self.c,self.d,X)
    end
    self.buf = sub(s, math.floor(#s/64)*64 + 1, #s)
    return self
    end

    local function md5_finish(self)
    local msgLen = self.pos
    local padLen = 56 - msgLen % 64

    if msgLen % 64 > 56 then padLen = padLen + 64 end

    if padLen == 0 then padLen = 64 end

    local s = char(128) .. rep(char(0),padLen-1) .. lei2str(bit_and(8*msgLen, 0xFFFFFFFF)) .. lei2str(math.floor(msgLen/0x20000000))
    md5_update(self, s)

    assert(self.pos % 64 == 0)
    return lei2str(self.a) .. lei2str(self.b) .. lei2str(self.c) .. lei2str(self.d)
    end

    ----------------------------------------------------------------

    function md5.new()
    return { a = CONSTS[65], b = CONSTS[66], c = CONSTS[67], d = CONSTS[68],
            pos = 0,
            buf = '',
            update = md5_update,
            finish = md5_finish }
    end

    function md5.tohex(s)
    return format("%08x%08x%08x%08x", str2bei(sub(s, 1, 4)), str2bei(sub(s, 5, 8)), str2bei(sub(s, 9, 12)), str2bei(sub(s, 13, 16)))
    end

    function md5.sum(s)
    return md5.new():update(s):finish()
    end

    function md5.sumhexa(s)
    return md5.tohex(md5.sum(s))
    end

-- ***** MD5 功能结束 ******


-- ***** datetime 功能开始 ******
    datetime = {}

    --[[
    |=========================================================================
    | 判断是否为闰年
    |=========================================================================
    | @param {int} year
    | @return boolean
    ]]
    function datetime.isLeapYear(year)
        return (year%4 == 0 and year%100 ~= 0) or (year%400 == 0)
    end

    --[[
    |=========================================================================
    | 获取UNIX时间戳
    |=========================================================================
    | 以UTC时间为标准
    |
    | @param {string} datetime 只支持格式'2016-10-13 01:01:01'
    | @return int
    ]]
    function datetime.toUnixTime(datetimeString)
        local datetimeTable = string.split(datetimeString,' ')
        local datepart = string.split(datetimeTable[1],'-')
        local timepart = string.split(datetimeTable[2],':')
        local sec = tonumber(timepart[3]) + tonumber(timepart[2])*60 + tonumber(timepart[1])*60*60 + (tonumber(datepart[3]) - 1)*86400

        local year = tonumber(datepart[1])
        local m = tonumber(datepart[2]) - 1
        for i=1,m do
            if i==1 or i==3 or i==5 or i==7 or i==8 or i==10 or i==12 then
                sec = sec + 31 * 86400
            elseif i==4 or i==6 or i==9 or i==11 then
                sec = sec + 30 * 86400
            else
                if datetime.isLeapYear(year) then
                    sec = sec + 29 * 86400
                else
                    sec = sec + 28 * 86400
                end
            end
        end

        local day = 0
        for i=1970,year-1 do
            if datetime.isLeapYear(i) then
                day = day + 366
            else
                day = day + 365
            end
        end
        sec = sec + day * 86400

        return sec
    end

    --[[
    |=========================================================================
    | 把UNIX时间戳转换成日期格式
    |=========================================================================
    | 以UTC时间为标准，输出格式'2016-10-13 01:01:01'
    |
    | @param {int} unixtime
    | @param {boolean} [returnTable] 可选，如果为true返回table
    | @return string|table
    ]]
    function datetime.toDatetime(unixtime, returnTable )
        local day = math.floor(unixtime/86400)
        local year = 1970
        while day >= 365 do
            if datetime.isLeapYear(year) then
                day = day - 366
                unixtime = unixtime - 366*86400
            else
                day = day - 365
                unixtime = unixtime - 365*86400
            end
            year = year + 1
        end

        _day = 0
        local month = 0
        for i=1,12 do
            local d
            if i==1 or i==3 or i==5 or i==7 or i==8 or i==10 or i==12 then
                d = 31
            elseif i==4 or i==6 or i==9 or i==11 then
                d = 30
            else
                if datetime.isLeapYear(year) then
                    d = 29
                else
                    d = 28
                end
            end
            _day = _day + d
            if day < _day then
                month = i
                unixtime = unixtime - (_day-d)*86400
                break
            end
        end

        day = math.floor(unixtime/86400)
        unixtime = unixtime - day*86400

        local h = math.floor(unixtime/3600)
        unixtime = unixtime - h*3600
        local m = math.floor(unixtime/60)
        unixtime = unixtime - m*60

        if returnTable then
            return {
                year = year,
                month = month,
                day = day+1,
                hour = h,
                min = m,
                second = unixtime,
            }
        end

        return string.format('%d-%02d-%02d %02d:%02d:%02d',year,month,day+1,h,m,unixtime)
    end

    --[[
    |=========================================================================
    | 获取服务器当前时间，返回标准格式时间，UNIX时间
    |=========================================================================
    | @return string,int
    ]]
    function datetime.now()
        local time = math.floor(UTC_TIME + GameRules:GetGameTime())
        return datetime.toDatetime(time),time
    end

    --[[
    |=========================================================================
    | 判断星期几，默认当前时间
    |=========================================================================
    | @param {int} week 区间[1,7]
    | @return boolean
    ]]
    function datetime.isWeek(week, datetimeString)
        local date = {}
        if datetimeString == nil then  
            date = datetime.toDatetime(math.floor(UTC_TIME + GameRules:GetGameTime()),true)
        else
            date = datetime.toDatetime(datetime.toUnixTime(datetimeString),true)
        end
        local y = date.year
        local m = date.month
        local d = date.day
        if m == 1 or m == 2 then
            m = m + 12
            y = y - 1
        end
        local w = math.floor((d+2*m+3*(m+1)/5+y+y/4-y/100+y/400) % 7)
        if w == 0 then w = 7 end
        -- print(currentWeek)        
        return w == week
    end

    --[[
    |=========================================================================
    | 根据日期获取星期几 返回1-7，默认当前时间
    |=========================================================================
    | @param {table} date or string
    | @return int
    ]]
    function datetime.getWeek(date)
        if date == nil then
            local time = math.floor(UTC_TIME + GameRules:GetGameTime())
            date = datetime.toDatetime(time,true)
        elseif type(date) == "string" then
            date = datetime.toDatetime(datetime.toUnixTime(date),true)
        end
        local y = date.year
        local m = date.month
        local d = date.day

        if m == 1 or m == 2 then
            y = y - 1
            m = m + 12
        end

        local w = (d+2*m+math.floor(3*(m+1)/5)+y+math.floor(y/4)-math.floor(y/100)+math.floor(y/400)) % 7 + 1

        if w == 0 then
            w = 7
        end

        return w
    end

    --[[
    |=========================================================================
    | 判断是否当月最后一天
    |=========================================================================
    | @return boolean
    ]]
    function datetime.isLastDay(datetimeAny)
        local data
        if type(datetimeAny) == "string" then
            data = datetime.toDatetime(datetime.toUnixTime(datetimeAny), true)
        elseif type(datetimeAny) == "table" then
            data = datetimeAny
        elseif type(datetimeAny) == "number" then
            data = datetime.toDatetime(datetimeAny, true)
        else
            print("Invalid datetime")
        end

        local i = data.month
        if i > 12 or i < 1 then
            print("Invalid datetime")
        end
        if i==1 or i==3 or i==5 or i==7 or i==8 or i==10 or i==12 then
            return data.day == 31
        elseif i==4 or i==6 or i==9 or i==11 then
            return data.day == 30
        else
            if datetime.isLeapYear(data.year) then
                return data.day == 29
            else
                return data.day == 28
            end
        end
    end

    --[[
    |=========================================================================
    | 获取当月最后一天，传入参数以 yyyy-MM 开头
    |=========================================================================
    | @return boolean
    ]]
    function datetime.getLastDay(datetimeString)
        local i = tonumber(string.sub(datetimeString, 6, 7))
        if i==1 or i==3 or i==5 or i==7 or i==8 or i==10 or i==12 then
            return 31
        elseif i==4 or i==6 or i==9 or i==11 then
            return 30
        else
            if datetime.isLeapYear(tonumber(string.sub(datetimeString, 1, 4))) then
                return 29
            else
                return 28
            end
        end
    end

    --[[
    |=========================================================================
    | 获取增加x个月后的时间，一个月按30天计算
    |=========================================================================
    | @return string
    ]]
    function datetime.addMonth(datetimeString, month)
        return datetime.toDatetime(datetime.toUnixTime(datetimeString) + month * 30 * 24 * 3600)  
    end

    --[[
    |=========================================================================
    | 获取增加x天后的时间
    |=========================================================================
    | @return string
    ]]
    function datetime.addDay(datetimeString, day)
        return datetime.toDatetime(datetime.toUnixTime(datetimeString) + day * 24 * 3600)  
    end

    --[[
    |=========================================================================
    | 获取两个日期相差多少天，只取日期部分
    |=========================================================================
    | @return number
    ]]
    function datetime.getDaysByTwoDate(dateStart, dateEnd)
        local time1 = datetime.toUnixTime(string.sub(dateStart,1,10).." 00:00:00")
        local time2 = datetime.toUnixTime(string.sub(dateEnd,1,10).." 00:00:00")
        return math.floor((time2-time1)/(24*3600))
    end

    --[[
    |=========================================================================
    | 获取两个日期相差多少秒，结果可用于判定日期前后
    |=========================================================================
    | @return number
    ]]
    function datetime.subtracte(dateStart, dateEnd)        
        return datetime.toUnixTime(dateEnd) - datetime.toUnixTime(dateStart)
    end

-- ***** datetime 功能结束 ******
