elua: use custom string buffer impl for format (avoids multiple table allocs and calls to string.char)

This commit is contained in:
Daniel Kolesa 2014-05-09 11:37:52 +01:00 committed by Daniel Kolesa
parent 8738ca957d
commit a6e50fa9c7
1 changed files with 78 additions and 14 deletions

View File

@ -2,11 +2,10 @@
local ffi = require("ffi") local ffi = require("ffi")
local cast = ffi.cast local cast = ffi.cast
local new = ffi.new
ffi.cdef [[ local copy = ffi.copy
int isalnum(int c); local str = ffi.string
int isdigit(int c); local gc = ffi.gc
]]
local C = ffi.C local C = ffi.C
@ -166,16 +165,79 @@ end
-- string fmt -- string fmt
ffi.cdef [[
typedef struct _Str_Buf {
char *buf;
size_t len, cap;
} Str_Buf;
void *malloc(size_t);
void free(void*);
size_t strlen(const char *str);
int isalnum(int c);
int isdigit(int c);
]]
local char = string.char local char = string.char
local tconc = table.concat local tconc = table.concat
local fmt = string.format local fmt = string.format
local pcall = pcall local pcall = pcall
local error = error local error = error
local type = type local type = type
local tostr = tostring
local bytes = { ("cdeEfgGiopuxXsq"):byte() } local bytes = { ("cdeEfgGiopuxXsq"):byte() }
for i, v in ipairs(bytes) do bytes[v] = true end for i, v in ipairs(bytes) do bytes[v] = true end
local Str_Buf = ffi.metatype("Str_Buf", {
__new = function(self)
local r = new("Str_Buf")
r.buf = C.malloc(8)
r.len = 0
r.cap = 8
gc(r, self.free)
return r
end,
__tostring = function(self)
return str(self.buf, self.len)
end,
__index = {
free = function(self)
C.free(self.buf)
self.buf = nil
end,
clear = function(self)
self.len = 0
end,
grow = function(self, newcap)
local oldcap = self.cap
if oldcap >= newcap then return end
local buf = C.malloc(newcap)
copy(buf, self.buf, self.len)
C.free(self.buf)
self.buf = buf
self.cap = newcap
end,
append_char = function(self, c)
local len = self.len
self:grow (len + 1)
self.buf [len] = c
self.len = len + 1
end,
append_str = function(self, str, strlen)
local strp = cast("const char*", str)
strlen = strlen or C.strlen(strp)
local len = self.len
self:grow(len + strlen)
for i = 0, strlen - 1 do
self.buf[len + i] = strp[i]
end
self.len = len + strlen
end
}
})
getmetatable("").__mod = function(fmts, params) getmetatable("").__mod = function(fmts, params)
if not fmts then return nil end if not fmts then return nil end
if type(params) ~= "table" then params = { params } end if type(params) ~= "table" then params = { params } end
@ -184,20 +246,20 @@ getmetatable("").__mod = function(fmts, params)
local c local c
c, s = s[0], s + 1 c, s = s[0], s + 1
local argn = 1 local argn = 1
local nbuf = Str_Buf()
while c ~= 0 do while c ~= 0 do
if c == 37 then -- % if c == 37 then -- %
c, s = s[0], s + 1 c, s = s[0], s + 1
local nbuf = {}
while c ~= 0 and C.isalnum(c) ~= 0 do while c ~= 0 and C.isalnum(c) ~= 0 do
nbuf[#nbuf + 1] = c nbuf:append_char(c)
c, s = s[0], s + 1 c, s = s[0], s + 1
end end
if c == 36 then -- $ if c == 36 then -- $
c, s = s[0], s + 1 c, s = s[0], s + 1
local n = char(unpack(nbuf)) local n = tostr(nbuf)
nbuf = {} nbuf:clear()
while C.isdigit(c) ~= 0 or c == 45 or c == 46 do -- -, . while C.isdigit(c) ~= 0 or c == 45 or c == 46 do -- -, .
nbuf[#nbuf + 1] = c nbuf:append_char(c)
c, s = s[0], s + 1 c, s = s[0], s + 1
end end
if bytes[c] then if bytes[c] then
@ -205,10 +267,11 @@ getmetatable("").__mod = function(fmts, params)
buf[#buf + 1] = "$" buf[#buf + 1] = "$"
buf[#buf + 1] = char(c) buf[#buf + 1] = char(c)
else else
nbuf[#nbuf + 1] = c nbuf:append_char(c)
local idx = tonumber(n) or n local idx = tonumber(n) or n
local stat, val = pcall(fmt, "%" .. char(unpack(nbuf)), local stat, val = pcall(fmt, "%" .. tostr(nbuf),
params[idx]) params[idx])
nbuf:clear()
if stat then if stat then
buf[#buf + 1] = val buf[#buf + 1] = val
else else
@ -222,11 +285,12 @@ getmetatable("").__mod = function(fmts, params)
else else
while c ~= 0 and (bytes[c] or C.isdigit(c) ~= 0 while c ~= 0 and (bytes[c] or C.isdigit(c) ~= 0
or c == 45 or c == 46) do or c == 45 or c == 46) do
nbuf[#nbuf + 1] = c nbuf:append_char(c)
c, s = s[0], s + 1 c, s = s[0], s + 1
end end
local stat, val = pcall(fmt, "%" .. char(unpack(nbuf)), local stat, val = pcall(fmt, "%" .. tostr(nbuf),
params[argn]) params[argn])
nbuf:clear()
if stat then if stat then
buf[#buf + 1] = val buf[#buf + 1] = val
else else