You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
koreader/frontend/stringzutils.lua

323 lines
5.6 KiB
Lua

local ffi = require "ffi"
local bit = require "bit"
local band = bit.band
local bor = bit.bor
local rshift = bit.rshift
local lshift = bit.lshift
--[[
String Functions
strlen
strndup
strdup
strcpy
strlcpy
strlcat
strchr
strcmp
strncmp
strcasecmp
strncasecmp
strrchr
strstr
strpbrk
bin2str
--]]
function strcmp(s1, s2)
local s1ptr = ffi.cast("const uint8_t *", s1);
local s2ptr = ffi.cast("const uint8_t *", s2);
-- uint8_t
local uc1;
local uc2;
-- Move s1 and s2 to the first differing characters
-- in each string, or the ends of the strings if they
-- are identical.
while (s1ptr[0] ~= 0 and s1ptr[0] == s2ptr[0]) do
s1ptr = s1ptr + 1
s2ptr = s2ptr + 1
end
-- Compare the characters as unsigned char and
-- return the difference.
uc1 = s1ptr[0];
uc2 = s2ptr[0];
if (uc1 < uc2) then
return -1
elseif (uc1 > uc2) then
return 1
end
return 0
end
function strncmp(str1, str2, num)
local ptr1 = ffi.cast("const uint8_t*", str1)
local ptr2 = ffi.cast("const uint8_t*", str2)
for i=0,num-1 do
if str1[i] == 0 or str2[i] == 0 then return 0 end
if ptr1[i] > ptr2[i] then return 1 end
if ptr1[i] < ptr2[i] then return -1 end
end
return 0
end
function strncasecmp(str1, str2, num)
local ptr1 = ffi.cast("const uint8_t*", str1)
local ptr2 = ffi.cast("const uint8_t*", str2)
for i=0,num-1 do
if str1[i] == 0 or str2[i] == 0 then return 0 end
if ptr1[i] > ptr2[i] then return 1 end
if ptr1[i] < ptr2[i] then return -1 end
end
return 0
end
function strcasecmp(str1, str2)
local ptr1 = ffi.cast("const uint8_t*", str1)
local ptr2 = ffi.cast("const uint8_t*", str2)
local num = math.min(strlen(ptr1), strlen(ptr2))
for i=0,num-1 do
if str1[i] == 0 or str2[i] == 0 then return 0 end
if tolower(ptr1[i]) > tolower(ptr2[i]) then return 1 end
if tolower(ptr1[i]) < tolower(ptr2[i]) then return -1 end
end
return 0
end
function strlen(str)
local ptr = ffi.cast("uint8_t *", str);
local idx = 0
while ptr[idx] ~= 0 do
idx = idx + 1
end
return idx
end
function strndup(str,n)
local len = strlen(str)
local len = math.min(n,len)
local newstr = ffi.new("char["..(len+1).."]");
ffi.copy(newstr, str, len)
newstr[len] = 0
return newstr
end
function strdup(str)
-- In the case of a Lua string
-- create a VLA and initialize
if type(str) == "string" then
return ffi.new("uint8_t [?]", #str+1, str)
end
-- Most dangerous, assuming it's a null terminated
-- string.
local len = strlen(str)
local newstr = ffi.new("char[?]", (len+1));
local strptr = ffi.cast("const char *", str)
ffi.copy(newstr, ffi.cast("const char *", str), len)
newstr[len] = 0
return newstr
end
function strcpy(dst, src)
local dstptr = ffi.cast("char *", dst)
local srcptr = ffi.cast("const char *", src)
-- Do the copying in a loop.
while (srcptr[0] ~= 0) do
dstptr[0] = srcptr[0];
dstptr = dstptr + 1;
srcptr = srcptr + 1;
end
-- Return the destination string.
return dst;
end
function strlcpy(dst, src, size)
local dstptr = ffi.cast("char *", dst)
local srcptr = ffi.cast("const char *", src)
local len = strlen(src)
local len = math.min(size-1,len)
ffi.copy(dstptr, srcptr, len)
dstptr[len] = 0
return len
end
function strlcat(dst, src, size)
local dstptr = ffi.cast("char *", dst)
local srcptr = ffi.cast("const char *", src)
local dstlen = strlen(dstptr);
local dstremaining = size-dstlen-1
local srclen = strlen(srcptr);
local len = math.min(dstremaining, srclen)
for idx=dstlen,dstlen+len do
dstptr[idx] = srcptr[idx-dstlen];
end
return dstlen+len
end
function strchr(s, c)
local p = ffi.cast("const char *", s);
while p[0] ~= c do
if p[0] == 0 then
return nil
end
p = p + 1;
end
return p
end
function strrchr(s, c)
local p = ffi.cast("const char *", s);
local offset = strlen(p);
while offset >= 0 do
if p[offset] == c then
return p+offset
end
offset = offset - 1;
end
return nil
end
function strstr(str, target)
if (target == nil or target[0] == 0) then
return str;
end
local p1 = ffi.cast("const char *", str);
while (p1[0] ~= 0) do
local p1Begin = p1;
local p2 = target;
while (p1[0]~=0 and p2[0]~=0 and p1[0] == p2[0]) do
p1 = p1 + 1;
p2 = p2 + 1;
end
if (p2[0] == 0) then
return p1Begin;
end
p1 = p1Begin + 1;
end
return nil;
end
--[[
String Helpers
--]]
-- Given two null terminated strings
-- return how many bytes they have in common
-- this is for prefix matching
function string_same(a, b)
local p1 = ffi.cast("const char *", a);
local p2 = ffi.cast("const char *", b);
local bytes = 0;
while (p1[bytes] ~= 0 and p2[bytes] ~= 0 and p1[bytes] == p2[bytes]) do
bytes = bytes+1
end
return bytes;
end
-- Stringify binary data. Output buffer must be twice as big as input,
-- because each byte takes 2 bytes in string representation
local hex = strdup("0123456789abcdef")
function bin2str(to, p, len)
--print("bin2str, len: ", len);
local off1, off2;
while (len > 0) do
off1 = rshift(p[0], 4)
to[0] = hex[off1];
to = to + 1;
off2 = band(p[0], 0x0f);
to[0] = hex[off2];
to = to + 1;
p = p + 1;
len = len - 1;
-- print(off1, off2);
end
to[0] = 0;
end
local function bintohex(s)
return (s:gsub('(.)', function(c)
return string.format('%02x', string.byte(c))
end))
end
local function hextobin(s)
return (s:gsub('(%x%x)', function(hex)
return string.char(tonumber(hex, 16))
end))
end
return {
strchr = strchr,
strcmp = strcmp,
strncmp = strncmp,
strncasecmp = strncasecmp,
strcpy = strcpy,
strndup = strndup,
strdup = strdup,
strlen = strlen,
bintohex = bintohex,
hextobin = hextobin,
}