Lua HTTP 解析器

我正在尝试用 Lua 解析 HTTP POST 请求。我的实现方式可以工作,但会消耗大量的 CPU 资源。这很重要,因为它运行在嵌入式平台上。

我查看了其他的实现方式,但是它们无法适用于我,因为我的镜像硬件内存已经占满了,所以我不想使用其他的库。我自己编写了解析器,但是它使用了太多的系统资源。我的问题是如何优化这个解析器,以便减少 CPU 资源的使用。

这是一个基于 OpenWRT 的系统,所以我只有 Lua 5.1。这是核心函数,用来查找边界(在 str 变量中)。它会分块读取输入,并寻找解析器所需的边界。

另一个解决方案是使用 LUCI 库来进行重的计算,但是我不想将我的代码集成到 LUCI 中。

- 寻找模式(str),并将输入复制到输出,直到找到为止。
local function writeuntil(in_fp, str, out_fp)

    local buff = ""
    local ret = false
    local bs = 4096 --块大小。一次性读取的数据量

    local c = in_fp:read(bs)
    local strStartPos = 1

    while c do
        local blockLen = string.len(c) --不能确保整个块都被读取了,所以获取实际块的大小
        local found = string.find(c, str, 1, true) --尝试定位 str,这样我们就不必做太多的工作
        if (found ~= nil) then
            if found > 2 then
                out_fp:write(string.sub(c, 1, found - 1))
            end
            ret = true
            break --我们完成了
        else --尝试匹配 str 直到块的结尾
            local strPos = string.find(c, string.sub(str, strStartPos, strStartPos), 1, true) --尝试找到第一个字符
            if strPos then --块中有起始字符
                if (strPos > 1) then
                        out_fp:write(string.sub(c, 1, strPos - 1))
                end
                for i = strPos, blockLen do --迭代块
                    local ch = string.sub(c, i, i)
                    if ch == string.sub(str, strStartPos, strStartPos) then
                        buff = buff .. ch
                        if string.len(buff) == string.len(str) then
                            ret = true
                            break --我们完成了
                        end
                        strStartPos = strStartPos + 1
                    else --迷失方向。输出
                        if string.len(buff) > 0 then
                            out_fp:write(buff)
                            buff = ""
                        end
                        out_fp:write(ch)
                        strStartPos = 1
                    end
                end
            else
                out_fp:write(c)
            end
        end
        if ret then
            break
        end
        c = in_fp:read(bs) --读取下一个块
    end
    return ret
end
点赞
用户6098948
用户6098948

Egor,你是对的,但我最终采用了这种方案。它现在使用的 CPU 更少。这不是完美的,因此 scp 更快(尽管它是用 C 实现的)。

--查找一个模式(str),并将输入复制到找到的位置之前的输出中。
local function writeuntil(in_fp, str, out_fp)

    local buff = ""
    local ret = false
    local bs = 4096 --块大小。每次读取的数据量
    
    local c = in_fp:read(bs)
    local strStartPos = 1
    local lastStrPos = 1
    local needData = true

    while c do
        local blockLen = string.len(c) --不确定是否已经读取了整个块,因此获取实际块的大小。
        local found = string.find(c, str, 1, true) --尝试定位 str,以便我们不需要很多工作。
        if (found ~= nil) then
            if found > 1 then
                if #buff > 0 then
                    out_fp:write(buff)
                end
                out_fp:write(string.sub(c, 1, found - 1))
            end
            ret = true
            break --我们完成了
        else --尝试匹配 str,直到块的结尾
            local strPos = string.find(c, string.sub(str, strStartPos, strStartPos), lastStrPos, true) --尝试找到第一个字符
            if strPos then --块中存在一个起始字符
                out_fp:write(string.sub(c, lastStrPos, strPos - 1))
                for i = strPos, blockLen do --迭代块
                    local ch = string.sub(c, i, i)
                    if ch == string.sub(str, strStartPos, strStartPos) then
                        buff = buff .. ch
                        if string.len(buff) == string.len(str) then
                            ret = true
                            break --我们完成了
                        end
                        strStartPos = strStartPos + 1
                        lastStrPos = i + 1
                    else --迷失了轨迹。输出。
                        if string.len(buff) > 0 then
                            out_fp:write(buff)
                            buff = ""
                        end
                        out_fp:write(ch)
                        strStartPos = 1
                        if i == blockLen then
                            needData = true
                        else
                            lastStrPos = i + 1
                            needData = false
                        end
                        break
                    end
                end
            else
                if ret == false then
                    if string.len(buff) > 0 then
                        out_fp:write(buff)
                        buff = ""
                    end
                    out_fp:write(string.sub(c, lastStrPos))
                    lastStrPos = 1
                    needData = true
                else
                    break
                end
            end
        end
        if ret then
            break
        end
        if needData then
            c = in_fp:read(bs) --读取下一个块
            lastStrPos = 1
        end
    end
    return ret
end
2017-09-13 13:13:36