--:Minify:--
-- blake2s.lua
-- Pure Lua 5.2, 32-bit only, supports keyed hashing

local bit32 = require("bit32")
local band, bor, bxor = bit32.band, bit32.bor, bit32.bxor
local rshift, lshift = bit32.rshift, bit32.lshift

local MOD32 = 2^32

local function rotr(x, n)
    return bor(rshift(x, n), lshift(x, 32 - n))
end

local IV = {
    0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A,
    0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19
}

local SIGMA = {
    {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15},
    {14,10,4,8,9,15,13,6,1,12,0,2,11,7,5,3},
    {11,8,12,0,5,2,15,13,10,14,3,6,7,1,9,4},
    {7,9,3,1,13,12,11,14,2,6,5,10,4,0,15,8},
    {9,0,5,7,2,4,10,15,14,1,11,12,6,8,3,13},
    {2,12,6,10,0,11,8,3,4,13,7,5,15,14,1,9},
    {12,5,1,15,14,13,4,10,0,7,6,3,9,2,8,11},
    {13,11,7,14,12,1,3,9,5,0,15,4,8,6,2,10},
    {6,15,14,9,11,3,0,8,12,2,13,7,1,4,10,5},
    {10,2,8,4,7,6,1,5,15,11,9,14,3,12,13,0}
}

local function G(v, a, b, c, d, x, y)
    v[a] = (v[a] + v[b] + x) % MOD32
    v[d] = rotr(bxor(v[d], v[a]), 16)
    v[c] = (v[c] + v[d]) % MOD32
    v[b] = rotr(bxor(v[b], v[c]), 12)
    v[a] = (v[a] + v[b] + y) % MOD32
    v[d] = rotr(bxor(v[d], v[a]), 8)
    v[c] = (v[c] + v[d]) % MOD32
    v[b] = rotr(bxor(v[b], v[c]), 7)
end

local function compress(h, block, t, last)
    local v = {}
    for i = 1, 8 do v[i] = h[i] end
    for i = 1, 8 do v[i + 8] = IV[i] end

    v[13] = bxor(v[13], t)
    if last then
        v[15] = bxor(v[15], 0xFFFFFFFF)
    end

    local m = {}
    for i = 0, 15 do
        local p = i * 4 + 1
        m[i] =
            (block:byte(p) or 0) +
            ((block:byte(p + 1) or 0) * 0x100) +
            ((block:byte(p + 2) or 0) * 0x10000) +
            ((block:byte(p + 3) or 0) * 0x1000000)
    end

    for r = 1, 10 do
        local s = SIGMA[r]
        G(v,1,5,9,13,  m[s[1]],  m[s[2]])
        G(v,2,6,10,14, m[s[3]],  m[s[4]])
        G(v,3,7,11,15, m[s[5]],  m[s[6]])
        G(v,4,8,12,16, m[s[7]],  m[s[8]])
        G(v,1,6,11,16, m[s[9]],  m[s[10]])
        G(v,2,7,12,13, m[s[11]], m[s[12]])
        G(v,3,8,9,14,  m[s[13]], m[s[14]])
        G(v,4,5,10,15, m[s[15]], m[s[16]])
    end

    for i = 1, 8 do
        h[i] = bxor(h[i], v[i], v[i + 8])
    end
end

local function blake2s(msg, key)
    key = key or ""

    local h = {}
    for i = 1, 8 do h[i] = IV[i] end

    local outlen = 32 -- bytes
    h[1] = bxor(
        h[1],
        0x01010000 + lshift(#key, 8) + outlen
    )

    local t = 0

    if #key > 0 then
        local block = key .. string.rep("\0", 64 - #key)
        t = #key
        compress(h, block, t, false)
    end

    for i = 1, #msg, 64 do
        local block = msg:sub(i, i + 63)
        if #block < 64 then
            block = block .. string.rep("\0", 64 - #block)
        end
        t = t + math.min(64, #msg - i + 1)
        compress(h, block, t, i + 64 > #msg)
    end

    local out = ""
    for i = 1, 8 do
        out = out .. string.format("%08x", h[i])
    end
    return out
end

return blake2s