1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
|
local function esc(x)
local res = ""
for i=1, #x do
local c = x:sub(i,i)
if c == "\\" then
res = res .. "\\\\"
elseif c == "\01" then
res = res .. "\\a"
elseif c == "\02" then
res = res .. "\\b"
elseif c == "|" then
res = res .. "\\p"
else
res = res .. c
end
end
return res
end
local function unesc(x)
local res = ""
local i = 1
while i <= #x do
local c = x:sub(i,i)
if c == "\\" then
local c2 = x:sub(i+1,i+1)
if c2 == "\\" then res = res .. "\\" end
if c2 == "a" then res = res .. "\01" end
if c2 == "b" then res = res .. "\02" end
if c2 == "p" then res = res .. "|" end
i = i + 1
else
res = res .. c
end
i = i + 1
end
return res
end
local function genmsg(msg)
return "\01|"..msg.timestamp.."|"..esc(msg.sender).."|"..esc(msg.content).."|"..(msg.parent or "").."|"
end
local function parsemsg(readable)
if readable:read(1) ~= "\01" or readable:read(1) ~= "|" then
return false
end
local section = 0
local timestamp = ""
local sender = ""
local content = ""
local parent = ""
while true do
local c = readable:read(1)
if c == nil then
return false
end
if c == "|" then
section = section + 1
if section == 4 then return {timestamp=timestamp, sender=unesc(sender), content=unesc(content), parent=parent=="" and nil or tonumber(parent)} end
elseif section == 0 then
timestamp = timestamp .. c
elseif section == 1 then
sender = sender .. c
elseif section == 2 then
content = content .. c
else
parent = parent .. c
end
end
end
local function appendmsg(filename,msg)
local f = io.open(filename,"ab")
local res = f:seek("cur",0)
f:write(genmsg(msg))
f:close()
return res
end
local function getmsg(filename,id)
local f = io.open(filename,"rb")
f:seek("set",id)
local msg = parsemsg(f)
msg.id = id
f:close()
return msg
end
local function last_n(filename,n,id)
local res = {}
local f = io.open(filename,"rb")
local i = id or 0
while true do
i = i - 1
local pos = f:seek(id and "set" or "end",i)
local c = f:read(1)
if not c then
local res2 = {}
for i=#res,1,-1 do
table.insert(res2,res[i])
end
return res2
end
if c == "\01" then table.insert(res, getmsg(filename,pos)) end
if #res >= n or pos == 0 then
local res2 = {}
for i=#res,1,-1 do
table.insert(res2,res[i])
end
return res2
end
end
end
return {last_n = last_n, getmsg = getmsg, appendmsg = appendmsg}
|